◉CLAUDE LABJP
●2.1.296 — Subagents can set their own autoCompactWindow, and an env var pins the model used by workflow agents●AGENTS — Managed Agents gain dynamic workflows in beta: an agent can build a run for work with many pieces (Oct 9)●SONNET 4.5 — Retires on the Claude API on 11/30, 48 days left. Migrate to Sonnet 5.5●Q&A — A write-up shows how to stop a streaming call midway so a closed tab stops being billed●WORKTREE — Running claude --worktree on one repo in parallel: what to settle first, as one write-up lays it out●NEW — Stop making it reread the whole notes file: look up one section by number●2.1.296 — Subagents can set their own autoCompactWindow, and an env var pins the model used by workflow agents●AGENTS — Managed Agents gain dynamic workflows in beta: an agent can build a run for work with many pieces (Oct 9)●SONNET 4.5 — Retires on the Claude API on 11/30, 48 days left. Migrate to Sonnet 5.5●Q&A — A write-up shows how to stop a streaming call midway so a closed tab stops being billed●WORKTREE — Running claude --worktree on one repo in parallel: what to settle first, as one write-up lays it out●NEW — Stop making it reread the whole notes file: look up one section by number
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 DesktopCowork44Session Management3Claude Code261Logging4

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 retention—Policy 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-05-04
Claude Desktop in 2026 — Projects, MCP, and Extended Thinking in Daily Use
Notes from using Claude Desktop daily as an indie developer — when Projects, MCP, extended thinking, and Computer Use actually pay off, and how the desktop app compares to the web version in 2026.
◈ Cowork2026-09-18
How I decide between Cowork and Claude Code: by where approvals land
Cowork and Claude Code run the same Claude, so I stopped comparing features and started sorting work by one question: can a human approval interrupt this step, or not? Here is the line I draw and the code I keep on the unattended side.
⟐ Claude Code2026-09-03
Aligning Log Timezones at Display Time, or Fixing Them at Write Time
AdMob, the store reports, and my own logs each ended the day at a different moment. Here is how I went back and forth between display-side and write-side timezone handling, and what I settled on.
📚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