CLAUDE LABJP
RELEASE — Claude Code v2.1.248 shipped on August 27, followed a day later by v2.1.250, a one-line stabilizing release. It is the largest batch of changes this weekRESTRICTED — The new --restricted flag drops the built-in tools that run commands or code along with WebFetch, keeps file work inside the working directory, and refuses bypassPermissionsSECURITY — /ultrareview was uploading credential files such as prod.env, *.tfvars, and swap or backup copies like key.pem.tmp. Those now stay on your machineTOKENS — The Workflow tool's description shrank from roughly 5.7k tokens to about 1k, with the script-writing reference moved into a bundled workflow-authoring skillHARDWARE — Anthropic released the Model Hardware Standard as a research preview, a way to connect Claude to scientific, robotics, and manufacturing hardwareLIMITS — The 50% weekly limit increase runs through August 31 for Pro, Max, Team, and seat-billed Enterprise accounts, which leaves two daysRELEASE — Claude Code v2.1.248 shipped on August 27, followed a day later by v2.1.250, a one-line stabilizing release. It is the largest batch of changes this weekRESTRICTED — The new --restricted flag drops the built-in tools that run commands or code along with WebFetch, keeps file work inside the working directory, and refuses bypassPermissionsSECURITY — /ultrareview was uploading credential files such as prod.env, *.tfvars, and swap or backup copies like key.pem.tmp. Those now stay on your machineTOKENS — The Workflow tool's description shrank from roughly 5.7k tokens to about 1k, with the script-writing reference moved into a bundled workflow-authoring skillHARDWARE — Anthropic released the Model Hardware Standard as a research preview, a way to connect Claude to scientific, robotics, and manufacturing hardwareLIMITS — The 50% weekly limit increase runs through August 31 for Pro, Max, Team, and seat-billed Enterprise accounts, which leaves two days
Articles/Claude.ai
Claude.ai/2026-08-29Beginner

The 30-Day Session Cleanup Is Fixed, and It Made Me Rethink Where I Keep Records

Claude Desktop and Cowork sessions were quietly disappearing after 30 days until v2.1.248 fixed it. Here is how to check your own setup, what the new setting actually caps, and a small journal script that keeps one line per run outside the session.

Claude Desktop2Cowork36Session Management3Claude Code238Logging3

I was reading through the v2.1.248 change list top to bottom when one line stopped me: Claude Desktop and Cowork sessions had been disappearing after 30 days.

As an indie developer maintaining a few apps, I hand a handful of jobs to scheduled tasks every day — image exports, backups, that sort of thing — and I go back to them later to see what happened. The thing I was opening to do that was the session history in the app.

Now that I think about it, I cannot remember opening a session older than a month. It wasn't that I noticed them missing. It's that the possibility never crossed my mind.

What was happening, and what changed

The published changelog says the transcript cleanup was sweeping up sessions written by the desktop apps. After the fix, sessions are kept while they are in the app — unless your organization manages retention through policy, in which case policy wins.

The same release added a new setting, desktopSessionCleanupPeriodDays, to cap that exemption.

ItemBefore (through v2.1.247)After (v2.1.248 onward)
Desktop / Cowork sessionsSwept by transcript cleanup after 30 daysKept while they live in the app
Org-managed retentionPolicy takes precedence
Cap on the exemptionNoneSet with desktopSessionCleanupPeriodDays

The same release also fixed an agent view that would resurrect a weeks-old background session after the machine had been off, and a VSCode chat tab stuck on "No conversation found" when its session was never saved. Read together, this looks like a release that went after session lifetime and session state as one problem.

What matters is that none of this reaches backwards. Sessions that were already swept are not coming back. Everything from here on is what gets protected.

Check whether your setup is affected

The only thing you need to do first is read your version.

claude --version

Anything at 2.1.248 or above has the fix. The 2.1.250 build that landed the next day is a stability-only release, so being there is plenty. How you upgrade depends on how you installed it — npm, native binary, or a package manager — so use whichever path you already have.

If you want the broader tour of what the desktop app can do, the walkthrough of the desktop app is a better fit. This piece stays on the records that were quietly going away.

desktopSessionCleanupPeriodDays is not "how long to keep things"

The name reads like a retention period, but the changelog wording is that it caps the exemption. It puts a ceiling on how long a session may stay excluded from cleanup, rather than promising to keep it forever.

{
  "desktopSessionCleanupPeriodDays": 180
}

After you write it, confirm it is actually being read. Claude Code settings do not warn you about a misspelled key — a one-character typo is silently ignored, which I wrote about in the piece on settings keys that vanish without a word. A setting that looks written but isn't in effect is the worst of both worlds.

Personally, I never felt much pull toward raising this number. The next section explains why.

Count how many days of sessions you actually have

Start by measuring. Claude Code stores session transcripts as JSONL, so a short script that counts files and the age of the oldest one is enough.

#!/usr/bin/env bash
# Count local session transcripts and the age of the oldest one
set -euo pipefail
ROOT="${1:-$HOME/.claude/projects}"
if [ ! -d "$ROOT" ]; then
  echo "not found: $ROOT"
  exit 1
fi
NOW=$(date +%s)
TOTAL=0; OVER30=0; OLDEST=0
while IFS= read -r f; do
  M=$(date -r "$f" +%s 2>/dev/null || stat -c %Y "$f")
  AGE=$(( (NOW - M) / 86400 ))
  TOTAL=$((TOTAL + 1))
  [ "$AGE" -gt 30 ] && OVER30=$((OVER30 + 1))
  [ "$AGE" -gt "$OLDEST" ] && OLDEST=$AGE
done < <(find "$ROOT" -type f -name '*.jsonl')
echo "root      : $ROOT"
echo "sessions  : $TOTAL"
echo "over 30d  : $OVER30"
echo "oldest    : ${OLDEST}d"

Here is the output from a run against five sample files I created with spread-out timestamps, just to confirm the script behaves:

root      : /home/me/lab/fake/projects
sessions  : 5
over 30d  : 2
oldest    : 45d

The storage path varies by environment. If the default above finds nothing, this usually locates it:

find "$HOME" -name '*.jsonl' -path '*claude*' 2>/dev/null | head

If over 30d sits at zero and never climbs, cleanup is probably still taking them. If that number grows normally, your machine is on the keeping side. One number turns "maybe things are disappearing" into something you can check.

Stop treating the session as the place records live

This is why I didn't reach for a bigger retention number.

A session transcript holds the entire conversation, which also makes it too heavy to search later. What I want to know about a scheduled job is whether last night's run went through or was skipped — not the full path that led there. Having everything stored and being able to read what you need are two different things.

So I started writing exactly one line per run, somewhere other than the session.

#!/usr/bin/env bash
# Always leave one line per run, outside the session
JOURNAL_DIR="${JOURNAL_DIR:-$HOME/run-journal}"
JOB="${1:?job name required}"; shift
 
mkdir -p "$JOURNAL_DIR"
JOURNAL="$JOURNAL_DIR/$(TZ=Asia/Tokyo date +%Y-%m).log"
STATUS="FAILED"          # if nothing happens at all, it lands as a failure
NOTE=""
 
write_line() {
  printf '%s\t%s\t%s\t%s\n' \
    "$(TZ=Asia/Tokyo date '+%Y-%m-%d %H:%M:%S')" "$JOB" "$STATUS" "$NOTE" >> "$JOURNAL"
}
trap write_line EXIT        # a killed run still leaves its line
 
if OUT=$("$@" 2>&1); then
  STATUS="SUCCESS"; NOTE=$(printf '%s' "$OUT" | tail -1 | cut -c1-120)
else
  CODE=$?
  if [ "$CODE" -eq 2 ]; then STATUS="SKIPPED"; else STATUS="FAILED"; fi
  NOTE="exit=$CODE $(printf '%s' "$OUT" | tail -1 | cut -c1-100)"
fi

Three things carry the design.

STATUS starts at FAILED. Success only overwrites it once the work is done, so a run that died halfway never disappears as "no record". Nothing is silently absent, which means the totals add up when you count later.

trap write_line EXIT is installed before anything runs. Without it, the runs that were force-killed are exactly the ones that leave no trace — the failure you most want to see becomes the one least likely to be recorded.

Exit code 2 is reserved for "skipped." A run that did nothing for a legitimate reason — the previous run is still going, there was nothing to process — should not be filed next to a real failure.

I ran all four endings to check:

./run_journal.sh backup-photos    bash -c 'echo "copied 128 files"'
./run_journal.sh thumbnail-batch  bash -c 'echo "already running"; exit 2'
./run_journal.sh sync-remote      bash -c 'echo "ssh: connection refused" >&2; exit 255'
./run_journal.sh crash-case       bash -c 'kill -9 $$'

And the journal it produced:

2026-08-29 15:08:32	backup-photos	SUCCESS	copied 128 files
2026-08-29 15:08:32	thumbnail-batch	SKIPPED	exit=2 already running
2026-08-29 15:08:32	sync-remote	FAILED	exit=255 ssh: connection refused
2026-08-29 15:08:32	crash-case	FAILED	exit=137

The last line is the one worth looking at. The run I killed with kill -9 still recorded itself as exit=137. Without the trap, that line simply would not exist.

The monthly roll-up is a one-liner:

awk -F'\t' '{c[$3]++} END {for (s in c) printf "%-8s %d\n", s, c[s]}' "$HOME"/run-journal/*.log
FAILED   2
SUCCESS  1
SKIPPED  1

Because skips were never folded into successes, those numbers read straight. The more work you delegate, the more it pays to count success, failure, and skip separately. The same thinking applies when you hand a long job to another session and wait for it, which I covered in the note on completion markers.

One caveat: recording a skip when a lock is held doesn't work everywhere. On a cloud-synced folder where deletes are refused, the lock stays behind and shuts out every later run. I measured that failure mode in the piece on locks in a shared folder.

One thing to do today

Run claude --version and see whether you are at 2.1.248 or above. If you are, your sessions are on the protected side now. If you aren't, treat the history you can currently see as something that will go away.

Then create this month's journal file. Next month you will have something to compare against. This is the nudge that finally got me to do it.


References: Claude Code v2.1.248 release notes / Claude Code changelog

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude.ai2026-03-20
Claude Development Best Practices Collection — Essential Techniques from 29 Premium Articles
A curated collection of best practices from all Claude Lab premium articles. Covers API design, Claude Code workflows, Cowork automation, monetization strategies, and prompt engineering.
Claude.ai2026-03-18
Claude Enterprise Now Available for Self-Serve Purchase — No Sales Call Required
Anthropic now lets any organization purchase Claude Enterprise directly on its website, no sales involvement needed. Get enterprise-grade AI — including Claude Code and Cowork — up and running same day.
Claude.ai2026-06-19
Pointing Claude Design at Your Codebase: Closing the Design-to-Implementation Loop Solo
The June 17 update lets Claude Design start from your local codebase, so generated assets reflect your existing components. Here is how I wire code-grounded generation into maintaining four sites' UI alone as an indie developer.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →