[BUG] Session becomes permanently unresumable (400 `server_tool_use.id` pattern) after switching a session from a third-party provider back to the Anthropic API

Open 💬 0 comments Opened Jul 14, 2026 by furkankoykiran

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

If a session is run under a third-party Anthropic-compatible provider (via a proxy that swaps ANTHROPIC_BASE_URL/auth) and then switched back to the real Anthropic API and resumed, the session can become permanently unresumable.

The provider I used (Z.AI / GLM) persisted an assistant turn containing a server_tool_use block whose id is in OpenAI style (call_...) instead of Anthropic's srvtoolu_.... On resume, Claude Code replays the full transcript to the Anthropic Messages API, which rejects the whole request with a 400:

messages.396.content.3.server_tool_use.id: String should match pattern '^srvtoolu_[a-zA-Z0-9_]+$'

Because the entire transcript is re-sent on every turn, the 400 repeats for every subsequent message. The session is bricked — there is no in-product way to recover; I had to hand-edit the session .jsonl.

Two distinct defects are involved in the same persisted turn:

  1. Foreign server_tool_use.id. The Anthropic API enforces ^srvtoolu_[a-zA-Z0-9_]+$ for server_tool_use.id; the persisted id has a call_ prefix. (Note: client tool_use ids in the same transcript are also call_... and are accepted fine — the strict pattern only applies to server_tool_use.)
  2. tool_result inside an assistant-role message. The provider split one logical turn into three consecutive role: assistant records: a server_tool_use, a text summary, and a tool_result that references the same id. A tool_result block inside an assistant message is invalid on the Anthropic API, so fixing only defect #1 surfaces a second 400.

The broader robustness gap is provider-agnostic: a single malformed historical block makes an entire session unresumable with a hard 400 loop and no recovery path.

What Should Happen?

Switching a session back to the Anthropic API and resuming should not hard-fail. Options, roughly in order of preference:

  • On resume/replay, normalize or drop tool blocks that can't be sent to the active endpoint (e.g. foreign-prefixed server_tool_use ids, tool_result blocks in assistant-role messages), rather than forwarding them verbatim.
  • Detect a mismatch between the persisted transcript's provider and the active endpoint and surface a recoverable, actionable error instead of a raw API 400.
  • Provide a repair path (e.g. claude --resume self-heals, or a documented transcript-sanitize command).

Error Messages/Logs

# Weekly limit on the third-party provider is what prompted switching back to Anthropic:
API Error: Request rejected (429) · [1310][Weekly/Monthly Limit Exhausted...]

# After `ccs anthropic` + resume, every message attempt returns:
API Error: 400 messages.396.content.3.server_tool_use.id: String should match pattern '^srvtoolu_[a-zA-Z0-9_]+$'

Steps to Reproduce

  1. Run a Claude Code session under a third-party Anthropic-compatible provider (here Z.AI / GLM, reached by pointing the base URL + auth at the provider). During the session, trigger a provider-specific server tool (in my case an image-analysis tool named analyze_image).
  2. The provider persists an assistant turn into the session .jsonl containing a server_tool_use block with an id like call_888211ac23974faf8935a8fb, plus a paired tool_result block inside an assistant-role record.
  3. Switch the session back to the real Anthropic API (I use a small ccs anthropic helper that swaps the provider config) and resume the same session.
  4. Send any message. The client replays the full transcript and the Anthropic API returns 400 ... server_tool_use.id: String should match pattern '^srvtoolu_[a-zA-Z0-9_]+$'.
  5. Every subsequent message returns the same 400. The session cannot be resumed.

Minimal shape of the offending persisted record (one .jsonl line, trimmed):

{"type":"assistant","message":{"role":"assistant","content":[
  {"type":"server_tool_use","id":"call_888211ac23974faf8935a8fb","name":"analyze_image","input":{}}
]}}

...followed a couple of lines later by, also under role: assistant:

{"type":"assistant","message":{"role":"assistant","content":[
  {"type":"tool_result","tool_use_id":"call_888211ac23974faf8935a8fb","content":"..."}
]}}

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.206 (Claude Code)

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

Manual workaround that unblocked me (edit the session .jsonl under ~/.claude/projects/<slug>/):

  1. Find offending blocks — any server_tool_use.id not matching ^srvtoolu_, and any tool_result in a role: assistant message.
  2. Convert both blocks to plain text blocks, preserving their content (the human-readable tool output is already present as text, so no information is lost).
  3. This clears both the strict-id rejection and the misplaced-tool_result rejection in one pass; the session resumes normally.

Notes for triage:

  • A third-party provider (Z.AI / GLM), reached via a base-URL/auth proxy, was in the loop for part of the session. That setup is outside Anthropic's supported providers, so the specific foreign id shape originates there. I'm reporting it because Claude Code persists provider output verbatim and then forwards it to a different endpoint on resume, and because a single bad historical block bricking a whole session with no recovery path seems worth hardening against regardless of provider.
  • In my ~1,200-line transcript there was exactly one such offending pair; the hundreds of ordinary client tool_use/tool_result pairs with call_ ids replayed fine, which is what isolates the failure to server_tool_use specifically.

View original on GitHub ↗