[BUG] Session JSONL appender silently dies mid-session while the process keeps running — history becomes memory-only and is lost on resume

Status Closed — not planned
Reported on v2.1.217
Maintainer reply None cached
Activity 1 comment · opened Jul 23, 2026 · closed Jul 23, 2026

[BUG] Session JSONL appender silently dies mid-session while the process keeps running — history becomes memory-only and is lost on resume

Summary

In long-lived interactive sessions, the process that appends turns to the top-level session transcript (~/.claude/projects/<proj>/<sid>.jsonl) stops writing partway through a session that is otherwise still fully alive and working. The same process keeps writing its sibling files — <sid>/subagents/*.meta.json and <sid>/tool-results/*.txt — for days afterward, so this is not a crash and not a filesystem rollback: the top-level transcript appender specifically has died while the rest of the process runs on.

Because the live conversation continues only in the process's memory after that point, the on-disk transcript is a stale fragment. The loss is invisible until the tab is slept/closed and --resumed, at which point the replay contains only the fragment and everything after the freeze is gone.

This is distinct from the truncation/write-delay issues below (see "Not a duplicate of").

Signature (how to recognize it)

For an affected session, on disk:

  • The top-level <sid>.jsonl last-modified time and last timestamped user/assistant turn are frozen at some early point…
  • …while <sid>/tool-results/*.txt and <sid>/subagents/*.meta.json for the same session keep getting written for hours or days after that freeze.
  • No unclean shutdown occurred inside the loss window (verified against boot times / last).
  • The transcript never got smaller on disk — the missing tail was never written at all, so tail-truncation recovery (#53821) does not apply.

Concrete case (Claude Code v2.1.x, Linux):

  • Session froze its .jsonl 3 minutes into what became a 6-day session (last on-disk turn ~Jul 14 11:24).
  • That same session's tool-results/*.txt were still being written Jul 19, and subagents/*.meta.json Jul 20 — 5–6 days past the transcript freeze.
  • On a machine-wide check for one affected day: zero .jsonl writes landed anywhere, while tool-results//subagents/ writes for live sessions landed fine.
  • Observed across ~12 sessions since ~late June, disproportionately the long-lived, always-open tabs. Reproduces across multiple v2.1.x releases (seen through 2.1.217).

Impact

  • Silent, unrecoverable history loss. Nothing on disk ever held the lost turns, so no restore/backup tool can recover them.
  • The user has no signal: the session looks healthy (it keeps responding), and even the transcript file's mtime keeps moving, because the dead-writer process still appends untimestamped metadata records (last-prompt, ai-title, mode, permission-mode) on resume/close — so mtime is a false health indicator.
  • Discovered only on resume, typically after deliberately sleeping/closing the tab — i.e. exactly when the memory-only tail is destroyed.

Steps to reproduce

Not yet deterministically reproducible; it correlates with long-lived sessions kept open for days. Detection is reliable even without a repro:

  1. Pick a session that has been open a long time.
  2. Compare stat -c %y <sid>.jsonl and the last timestamped user/assistant entry inside it against the newest mtime under <sid>/tool-results/ and <sid>/subagents/.
  3. If the sibling directories are hours/days newer than the transcript's last real turn, the appender for that session has died.

Expected behavior

  • The transcript appender should never stop while the session runs; if a write fails, it should retry/reopen rather than silently give up for the remainder of the session.
  • Failing that, Claude Code should detect that its own transcript writes have stalled (it is still writing sibling files, so it can compare) and surface a warning, so the session can be exported before its memory-only tail is lost.

Not a duplicate of

  • #58463 (write delay: tool_use not written until tool_use_result arrives) — there the event eventually lands; here the appender dies permanently and later turns never land at all.
  • #75435 (frozen JSONL after compaction, breaking hooks that read transcript_path) — same frozen-transcript symptom, but that report ties the freeze to a compaction event and is scoped to hook staleness; this occurs without a compaction boundary and results in permanent user-facing history loss.
  • #53821 (closed; transcript truncated by power loss / abrupt termination) — that is tail corruption from an unclean write; here there is no unclean shutdown and the tail was never written, so "load from last valid entry" recovers nothing.

Environment

  • Claude Code 2.1.x (observed through 2.1.217), Linux, interactive TTY sessions.
  • Reproduced across multiple v2.1.x releases over ~4 weeks.

Diagnostic snippet

# Flag sessions whose transcript is frozen while the process keeps working:
for js in ~/.claude/projects/*/*.jsonl; do
  [ -d "${js%.jsonl}" ] || continue
  t_last=$(python3 - "$js" <<'PY'
import json,sys
last=0
for l in open(sys.argv[1],errors="ignore"):
    try: e=json.loads(l)
    except: continue
    if e.get("timestamp") and e.get("type") in ("user","assistant"):
        last=e["timestamp"]
print(last)
PY
)
  sib=$(find "${js%.jsonl}"/{tool-results,subagents} -type f -printf '%TFT%TT\n' 2>/dev/null | sort | tail -1)
  [ -n "$sib" ] && [ "$sib" \> "$t_last" ] && echo "FROZEN: $js  last-turn=$t_last  sibling=$sib"
done

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗