Session JSONL corrupted by non-atomic append when process killed mid-write — `--resume` fails with JSON parse error
Summary
When the Claude Code process is terminated mid-write to its session JSONL (e.g. SIGKILL from parent, stdio pipe closed on VS Code Remote-SSH disconnect, laptop sleep breaking the tunnel), the partially-written record leaves a truncated stub on disk. The next append then starts writing from the same file position with no newline separator, producing two concatenated JSON objects on a single line that parse as neither. claude --resume <session-id> fails with a cryptic JSON Parse error: Expected ':' before value in object property definition and the session cannot be resumed.
Environment
- Claude Code VS Code extension 2.1.118 (linux-x64); binary 2.1.117 / 2.1.118
- Linux remote accessed via VS Code Remote-SSH
- Trigger in our case: Remote-SSH tunnel drop over Tailscale / laptop sleep while Claude was streaming a response
Expected
Session files should remain parseable as newline-delimited JSON regardless of when the process is terminated. A partially-written record should be either fully absent or fully present — never a truncated stub followed by another record on the same line.
Actual
Example corrupt line (redacted, one bad line out of ~2800):
{"type":"pr-link","sessionId":"<redacted>","prNu{"parentUuid":"<redacted>","isSidechain":false,"promptId":"<redacted>","type":"user", ...}
The first ~74 bytes are a truncated pr-link record whose write was cut off at "prNu. The subsequent record was then appended to the same line with no separator. JSON.parse fails on the whole line; --resume bails out. Only the last-written line is corrupt — all prior records parse correctly.
Proposed fix
Make record appends atomic. Minimal-change options:
- Build the full record bytes (record + trailing
\n) in memory and issue a singlewrite(2)on anO_APPENDfd. Much less likely to tear than bufferedfwrite+fflushpairs. - For larger records, write to a sibling
.tmpand commit via rename-append.
Either prevents the "two records on one line" pattern.
Workaround
External repair script that validates the JSONL line-by-line, locates the inner {" split boundary on the one corrupt line, keeps whichever half is valid JSON, and drops the truncated stub. Safe to run idempotently. Happy to share the ~100 lines of Python if useful.
Severity
Minor but surfaces badly. Only the last record is ever affected, but the resume failure shows up as a JSON parse error with no obvious next step, and losing the active conversation context is disruptive enough that users blame the disconnect rather than a file-write bug.
Related issues
- #49876 — similar family (non-atomic JSONL appends), different manifestation: null-byte runs on WSL2 causing silent context truncation. Ours is a single-process kill on Linux/Remote-SSH causing loud parse-error failure.
- #20992 → #20637 — both closed as duplicates. About concurrent-writer races on macOS (
~/.claude.jsonand subagent workloads). Ours is single-writer.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗