Last turn Assistant messages and `turn_duration` may not be written to session transcript after /compact. Fragile/flaky, see repro inside.
Version: 2.1.280, 2.1.283 (macOS)
What happens
Sometimes a turn finishes in the TUI (the reply is shown and the idle prompt returns), but the session JSONL stops after the prompt's attachment and metadata rows.
The final assistant row, the system/turn_duration row, or both, are missing.
They appear only on a later transcript write, such as the next prompt; if nothing else happens, they are never written.
It is fragile: small changes in configuration or conversation shape make it go away, so the conditions below are what we could recreate, not necessarily the root cause.
Conditions under which we recreated multiple times (mostly reliably)
In our runs, it happened only when all of these held:
ANTHROPIC_BASE_URLpointed at a gateway / reverse proxy: this appears to impact timings / race condition- The total-tokens reminder was on (
CLAUDE_CODE_TOTAL_TOKENS_REMINDER=padded-countdown; in our tests this is also the default when account feature flags are not fetched). Withoff, the rows were written. - The session was resumed with
--continue - Run
/compactbefore the next prompt. - Send a follow-up prompt to initiate a turn
- The follow-up turn must have made consecutive tool calls with no text in between.
Repro
# A stock reverse proxy in front of the real API:
docker run -d --rm -p 127.0.0.1:8377:8377 caddy:2.11.4-alpine \
caddy reverse-proxy --from :8377 --to https://api.anthropic.com --change-host-header
export ANTHROPIC_BASE_URL=http://127.0.0.1:8377
export CLAUDE_CODE_TOTAL_TOKENS_REMINDER=padded-countdown
mkdir -p ~/cc-repro && cd ~/cc-repro
claude --allowedTools 'Bash(echo:*)'
# > I am reproducing a Claude Code transcript-writing bug, which needs a turn made of consecutive tool calls
# with no text in between. Please run `echo one` and then `echo two` as two separate Bash calls, one after
# the other (not in parallel), without writing any text before or between them. After both finish, reply with just: done
# > /exit
claude --continue
# > /compact
# > hello, just say hi back
# The reply shows and the turn ends in the TUI. Leave claude open, wait a minute, then in another terminal:
jq -c '{type, subtype, content: (.message.content | if type == "array" then map(.type) else . end)}' \
"$(ls -t ~/.claude/projects/*cc-repro*/*.jsonl | head -1)" | tail -n 16
Transcript after the compact boundary (2.1.283, 60+ s after "Hi!" was shown)
{"type":"system","subtype":"compact_boundary","timestamp":"2026-09-26T00:20:46.338Z"}
{"type":"user","content":"This session is being continued from a p…"}
{"type":"user","content":"<command-name>/compact</command-name>…"}
{"type":"user","content":"<local-command-stdout>Compacted (ctr…"}
{"type":"attachment","attachment":"agent_listing_delta"}
{"type":"attachment","attachment":"mcp_instructions_delta"}
{"type":"attachment","attachment":"environment"}
{"type":"attachment","attachment":"model"}
{"type":"file-history-snapshot"}
{"type":"user","timestamp":"2026-09-26T00:20:49.312Z","content":"hello, just say hi back"}
{"type":"attachment","attachment":"auto_mode"}
{"type":"attachment","attachment":"total_tokens_reminder"}
{"type":"attachment","attachment":"session_context"}
{"type":"attachment","attachment":"date"}
{"type":"attachment","attachment":"credential_org"}
{"type":"attachment","attachment":"remote_session_change"}
{"type":"attachment","attachment":"prompt_snapshot"}
{"type":"last-prompt"}
{"type":"ai-title"}
{"type":"mode"}
{"type":"permission-mode"}
{"type":"atis-latch"}
{"type":"cost-state"}
There is no assistant row and no turn_duration row for "hello".
In another run, the assistant row landed and only turn_duration was missing.
What happens next
If we continue the conversation with more turns, the missing rows are eventually written to the jsonl. Apparently they accumulated in-memory, but never get flushed to jsonl transcript.
Expected
The rows for a turn are written when the turn ends.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗