CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 doesn't suppress mid-conversation-system-2026-04-07, breaks local Ollama backends
Description
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 does not suppress the mid-conversation-system-2026-04-07 anthropic-beta feature. When this beta is active and the backend is a non-Anthropic, Anthropic-Messages-compatible server (tested against Ollama 0.31.1's /v1/messages endpoint, via ANTHROPIC_BASE_URL), the resulting mid-conversation system-role message breaks any downstream chat template that only expects system as the first message — which is effectively all of them, since this is a Claude-specific capability.
This looks related to but distinct from #22893, #11960, and #20031 (all closed, Nov 2025–Apr 2026) — those covered the disable flag failing to suppress other, older beta headers (advanced-tool-use-2025-11-20, etc.). Reproduced today, after all three of those fixes shipped, so the fix didn't generalize to this specific (newer) beta header.
Steps to reproduce
- Point Claude Code at a local Anthropic-Messages-compatible backend, e.g. Ollama 0.31.1:
````
export ANTHROPIC_BASE_URL=http://localhost:11434
export ANTHROPIC_AUTH_TOKEN=ollama
export ANTHROPIC_API_KEY=""
- Run any prompt that triggers a tool call, e.g.:
````
claude --model <any-local-model> -p "Use the Read tool to read some_file and tell me its contents"
- Capture the outgoing request (e.g. via tcpdump on loopback) and inspect the
messagesarray.
Expected
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 prevents the mid-conversation-system-2026-04-07 beta (and its associated anthropic-beta header / mid-conversation system-role message) from being sent, OR the request degrades gracefully against non-Anthropic backends.
Actual
- The
anthropic-betaheader still includesmid-conversation-system-2026-04-07(and others:claude-code-20250219,interleaved-thinking-2025-05-14,thinking-token-count-2026-05-13,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advisor-tool-2026-03-01,effort-2025-11-24) whether or notCLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1is set — confirmed directly, same failure both ways. - The
messagesarray contains a genuine mid-conversation entry with"role": "system"(not just the top-levelsystemfield), e.g.:
``json``
{"model":"...","messages":[
{"role":"user","content":[...]},
{"role":"system","content":"<system-reminder>\n...\n</system-reminder>"}
], "system":[...], ...}
- Against a backend using a standard Jinja chat template (tested: Mistral/Devstral via Ollama), this causes a hard error:
````
Jinja Exception: Only user, assistant and tool roles are supported, got system.
- Against a looser template (tested: Qwen2.5-Coder via Ollama, Go template), the request doesn't error, but the model silently fails to follow tool-call formatting instructions in the same turn — plausibly because the injected mid-conversation role corrupts context the template wasn't built to handle. Not confirmed as definitively causal, but consistent across repeated tests.
Environment
- Claude Code: 2.1.201 (external, sdk-cli)
- SDK: 0.94.0 (node, v26.3.0)
- OS: Linux x64
- Backend: Ollama 0.31.1,
/v1/messagesAnthropic-compatibility endpoint - Models tested: qwen2.5-coder:14b (custom tag), hf.co/unsloth/GLM-4.7-Flash-REAP-23B-A3B-GGUF, hf.co/unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF
Notes
This isn't a request for Claude Code to officially support arbitrary local backends — it's specifically about CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 not doing what it says for this beta header, which is the documented escape hatch for exactly this scenario.