Last turn Assistant messages and `turn_duration` may not be written to session transcript after /compact. Fragile/flaky, see repro inside.

Status Open
Reported on v2.1.280
Maintainer reply None cached
Activity 1 comment · opened Sep 26, 2026

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_URL pointed 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). With off, the rows were written.
  • The session was resumed with --continue
  • Run /compact before 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.

View original on GitHub ↗

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