Claude Code v2.1.154 Broke Every Third-Party Provider. Here's What Happened.
On May 28, 2026, Anthropic shipped a Claude Code update that broke every non-Anthropic API provider within hours. A post-mortem on what went wrong, the fallout, and the compatibility safeguards we added to claude-multi.
Claude Code v2.1.154 Broke Every Third-Party Provider. Here's What Happened.
Claude Code v2.1.154 Broke Every Third-Party Provider. Here’s What Happened.
On May 28, 2026, Anthropic shipped Claude Code v2.1.154. Within hours, every non-Anthropic API provider stopped working. GLM, DeepSeek, MiniMax, Kimi, Qwen, MiMo — all of them returned the same error:
API Error: 422 {"detail":[{"type":"literal_error","loc":["body","messages",1,"role"],"msg":"Input should be 'user' or 'assistant'","input":"system","ctx":{"expected":"'user' or 'assistant'"}}]}If you’re using claude-multi to run Claude Code against a 3rd-party provider, you probably hit this.
What changed
Claude Code v2.1.150 introduced a new Anthropic beta: mid-conversation-system-2026-04-07. It’s been in every version since. This beta changes how Claude Code sends system instructions to the API.
Before, Claude Code used the top-level system parameter for system prompts. Every Anthropic-compatible API supports this:
{ "model": "claude-sonnet-4-20250514", "system": "You are a helpful assistant.", "messages": [ {"role": "user", "content": "Hello"} ]}After the beta, Claude Code also sends system instructions as messages with role: "system" inside the messages array:
{ "model": "claude-sonnet-4-20250514", "system": "You are a helpful assistant.", "messages": [ {"role": "user", "content": "Hello"}, {"role": "system", "content": "Remember: be concise"}, {"role": "assistant", "content": "Hi! How can I help?"} ]}The beta is opt-in via the anthropic-beta header. Claude Code enables it automatically for first-party API calls (api.anthropic.com). For third-party providers, it should be disabled. But the detection logic has a gap.
Why providers reject it
The Anthropic Messages API spec allows role: "system" in the messages array when the beta header is present. Without the header, it’s not valid. Most third-party providers implement the base spec without beta features. Their validators reject any role that isn’t "user" or "assistant", and return 422.
Claude Code has a fallback — but it’s incomplete
Anthropic anticipated this. Claude Code has built-in retry logic that detects when a provider rejects the mid-conversation-system beta. When it catches the error, it removes the beta header and falls back to putting system instructions inside <system-reminder> blocks in user messages.
The detection function (minified, from the binary):
function pP8(error) { if (!Yy) return false; if (!(error instanceof APIError) || error.status !== 400) return false; // ... pattern matching on error message}error.status !== 400. The fallback only triggers on HTTP 400. Third-party providers return HTTP 422 for validation errors. The retry logic never fires.
This is the bug. The beta feature itself is fine for first-party API. The error handling checks for one specific status code when providers use a different one for the same error.
What actually happens
- User runs
claude-glm(or any claude-multi instance) - Claude Code detects it’s using a third-party API
- The
mid-conversation-systembeta should be disabled, but the detection has edge cases - Claude Code sends a request with
role: "system"in the messages array - Provider returns HTTP 422
- Claude Code’s fallback checks
error.status !== 400— doesn’t match 422 - Error surfaces to the user
- Conversation is dead
If the provider had returned 400 instead of 422, the fallback would have kicked in.
How we fixed it in claude-multi
Three layers.
Layer 1: Pin the Claude Code version
We maintain a pinned Claude Code installation at ~/.claude-multi/bin/. All claude-multi instances use this instead of the global claude binary. Currently pinned to v2.1.153, the last version before the breakage.
npm install --prefix ~/.claude-multi/bin @anthropic-ai/claude-code@2.1.153The getClaudePath() function checks this path first, before falling back to which claude.
Layer 2: Disable auto-updates
We added DISABLE_AUTOUPDATER=1 and DISABLE_UPDATES=1 to every provider template’s environment variables. Claude Code won’t auto-update to a broken version.
From src/templates.ts:
const PROVIDER_COMMON_ENV: Record<string, string> = { DISABLE_AUTOUPDATER: "1", DISABLE_UPDATES: "1",};These get merged into every instance’s settings.json when created via a provider template.
Layer 3: Doctor fix for existing instances
For instances created before the fix, we added a health check and repair system.
The health check detects two wrapper formats:
- Shell format (current):
exec "/path/to/claude" "$@" - Node.js format (legacy):
spawn("/path/to/claude", ...)
Both get flagged as version issues. The fix regenerates wrappers as clean shell scripts pointing to the pinned binary.
# CLIclaude-multi doctor check # show issuesclaude-multi doctor fix # auto-fix
# TUIclaude-multi # press ! for health screen, then f to fixThe TUI shows a banner when version issues are detected:
⚠ 1 error, 2 warnings — press ! to review ⚠ Some instances use a Claude version incompatible with 3rd-party APIs. Press ! to auto-fix.What providers should do
Return HTTP 400, not 422, for message validation errors. Claude Code’s existing fallback handles 400. This is the fastest path to compatibility without waiting for a Claude Code patch.
What Anthropic should do
Two things:
- Broaden the status code check in the fallback logic.
error.status >= 400 && error.status < 500instead oferror.status !== 400. - Provide an env var to disable specific betas.
CLAUDE_CODE_DISABLE_MID_CONVERSATION_SYSTEM=1would let users opt out without downgrading.
The broader issue
This is the second time in recent months that a Claude Code update has broken third-party provider compatibility. The first was a change in how model names are validated. Anthropic tests against their own API, and third-party compatibility is incidental.
If you rely on third-party providers, pin your Claude Code version and disable auto-updates.
TL;DR
- Claude Code v2.1.150+ sends
role: "system"in the messages array (new beta feature) - Third-party providers reject it with HTTP 422
- Claude Code’s fallback only handles HTTP 400
- Fix: pin to v2.1.153, disable auto-updates, run
claude-multi doctor fix