Session rename mid-server-tool-call injects a turn that permanently corrupts the transcript (400 on every future prompt)
Summary
Renaming a session (custom title) while a server_tool_use call (e.g. the built-in advisor tool) is in flight injects a system-reminder as a synthetic user turn in the transcript, landing it between the server_tool_use block and its matching advisor_tool_result block. Because server-tool call + result must live in the same API message, this splits them across two messages on the next resume/request, and the Anthropic API rejects the entire history with a 400 on every subsequent turn — permanently breaking the session until the transcript file is hand-edited.
Reproduction
- Start a session and trigger a server-side tool call that returns asynchronously (in our case, the
advisortool). - While the call is in flight, rename the session (set a custom title) — e.g. via whatever UI/flow assigns
customTitle/agent-name. - Let the tool call resolve and continue the conversation, then send further prompts.
- Every subsequent prompt now fails with:
``tool_use_id
API Error: 400 messages.N.content.0: unexpected found in advisor_tool_result blocks: srvtoolu_XXXX. Each advisor_tool_result block must have a corresponding server_tool_use block before it.``
Root cause (confirmed by inspecting the session .jsonl)
In the broken transcript, the three lines around the failure were:
{"type":"assistant", ..., "uuid":"4320b59f-...", "message":{"content":[{"type":"server_tool_use","id":"srvtoolu_01YZBP7b9c3YoD3NXiLgXsVb","name":"advisor","input":{}}]}}
{"type":"user","isMeta":true, "parentUuid":"4320b59f-...", "message":{"role":"user","content":"<system-reminder>\nThe user named this session \"...\". This may indicate the session's focus or intent.\n</system-reminder>"}}
{"type":"assistant", "parentUuid":"<the user line above>", "message":{"content":[{"type":"advisor_tool_result","tool_use_id":"srvtoolu_01YZBP7b9c3YoD3NXiLgXsVb", ...}]}}
When the harness replays/merges the transcript into the API messages array, consecutive same-role entries are folded into one logical message (confirmed by other server_tool_use/advisor_tool_result pairs in the same file that have no interruption and work fine). The injected isMeta user turn breaks that fold here, producing:
- message A (assistant):
[..., server_tool_use] - message B (user):
[system-reminder text] - message C (assistant):
[advisor_tool_result, ...]
advisor_tool_result in message C no longer has its server_tool_use in the same message → 400 from the API → every future turn in the session fails identically, since the full (corrupted) history is resent each time.
Impact
- Session becomes permanently unusable for every future prompt, not just the one where the race happened.
- No user-facing recovery path — we had to manually locate the offending lines in
~/.claude/projects/<project>/<session>.jsonl, delete the injectedisMetasystem-reminder line, and repoint the following message'sparentUuidto restore a valid chain, then re-verify the merged message structure by hand.
Suggested fix
- Don't inject
system-reminder(or any synthetic) user turns while a server-side tool call is awaiting its result — queue the reminder and deliver it only once the current turn (tool_use + its result) is fully closed. - Defensively, on session load/resume, detect and either coalesce or drop any zero-content-value synthetic turn that would split a
server_tool_use/matching result pair, rather than sending it to the API and hard-failing.
Happy to share the (redacted) transcript snippet if useful.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗