[BUG] 2.1.154 appears to send messages[].role="system" to Anthropic-compatible providers, causing schema parse failures
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?
Claude Code 2.1.154 sends a system role message inside the messages[] array when using an Anthropic-compatible third-party endpoint via ANTHROPIC_BASE_URL.
This breaks providers that implement the standard Anthropic Messages API schema strictly, where messages[].role only accepts user or assistant, and system content is expected in the top-level system field.
This happens even with:
- A clean
CLAUDE_CONFIG_DIR - An empty working directory
claude -p "hi"- No project-specific files involved
The raw request captured via OTEL_LOG_RAW_API_BODIES shows:
{
"has_top_level_system": true,
"top_level_system_type": "array",
"message_roles": ["user", "system"],
"system_messages_in_messages": [
{
"index": 1,
"content_preview": "The following skills are available for use with the Skill tool: ..."
}
]
}
````
Earlier captures also showed plugin / SessionStart hook additional context serialized as:
```json
{
"role": "system",
"content": "SessionStart hook additional context: ..."
}
inside messages[].
The provider rejects this before model routing, so it affects multiple models.
What Should Happen?
When using ANTHROPIC_BASE_URL with an Anthropic-compatible provider, Claude Code should avoid sending messages[].role = "system" unless this format is known to be supported.
Expected behavior:
- Keep all system/context/skill/session-start content in the top-level
systemfield, or - Provide a compatibility flag that serializes system reminders as user-side
<system-reminder>text, or - Feature-detect provider support before sending mid-conversation system messages.
At minimum, there should be a compatibility mode for providers that implement the documented Messages API role schema strictly
Error Messages/Logs
API Error: 400 Failed to deserialize the JSON body into the target type:
messages[1].role: unknown variant `system`, expected `user` or `assistant`
Raw request body summary captured with `OTEL_LOG_RAW_API_BODIES=file:<dir>`:
{
"model": "deepseek-v4-pro",
"has_top_level_system": true,
"top_level_system_type": "array",
"message_roles": [
"user",
"system"
],
"system_messages_in_messages": [
{
"index": 1,
"content_preview": "The following skills are available for use with the Skill tool:\n\n- context7-mcp: ..."
}
]
}
The same error also occurs with another model on the same provider, so this appears to fail during request schema parsing before model execution.
Steps to Reproduce
- Use Claude Code 2.1.154 on Windows native.
- Configure a third-party Anthropic-compatible endpoint:
$env:ANTHROPIC_BASE_URL = "<third-party-anthropic-compatible-base-url>"
$env:ANTHROPIC_AUTH_TOKEN = "<redacted>"
$env:ANTHROPIC_MODEL = "deepseek-v4-pro"
$env:ANTHROPIC_DEFAULT_SONNET_MODEL = "deepseek-v4-pro"
$env:ANTHROPIC_DEFAULT_OPUS_MODEL = "deepseek-v4-pro"
$env:ANTHROPIC_DEFAULT_HAIKU_MODEL = "deepseek-v4-pro"
- Reproduce with a clean config directory and empty workdir:
$clean = Join-Path $env:TEMP ("cc-clean-" + [guid]::NewGuid().ToString())
New-Item -ItemType Directory $clean | Out-Null
$work = Join-Path $env:TEMP ("cc-work-" + [guid]::NewGuid().ToString())
New-Item -ItemType Directory $work | Out-Null
cd $work
$env:CLAUDE_CONFIG_DIR = $clean
claude -p "hi"
- Actual result:
API Error: 400 Failed to deserialize the JSON body into the target type:
messages[1].role: unknown variant `system`, expected `user` or `assistant`
- Capture the raw request body:
$dump = "$env:TEMP\cc-api-dump"
Remove-Item $dump -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory $dump | Out-Null
$env:CLAUDE_CODE_ENABLE_TELEMETRY = "1"
$env:OTEL_LOGS_EXPORTER = "console"
$env:OTEL_METRICS_EXPORTER = "none"
$env:OTEL_LOG_RAW_API_BODIES = "file:$dump"
$env:OTEL_LOGS_EXPORT_INTERVAL = "1000"
claude -p "hi"
- Inspect the request roles:
$req = Get-ChildItem $dump -Recurse -Filter "*.request.json" |
Sort-Object LastWriteTime |
Select-Object -Last 1
Get-Content $req.FullName -Raw | jq '{
model,
has_top_level_system: has("system"),
top_level_system_type: (.system | type),
message_roles: [.messages[].role],
system_messages_in_messages: [
.messages
| to_entries[]
| select(.value.role == "system")
| {
index: .key,
content_preview: ((.value.content | tostring)[0:1200])
}
]
}'
- Observed output includes:
{
"message_roles": ["user", "system"],
"system_messages_in_messages": [
{
"index": 1,
"content_preview": "The following skills are available for use with the Skill tool: ..."
}
]
}
Claude Model
Other
Is this a regression?
Yes, this worked in a previous version
Last Working Version
Unknown exact last working version. This started after updating to 2.1.154. 2.1.154 was published at 2026-05-28T15:40:50.400Z. The first observed failure occurred shortly afterward. Likely worked on 2.1.153 or earlier, but I have not fully bisected yet.
Claude Code Version
2.1.154 (Claude Code)
Platform
Other
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
A manual request to the same provider succeeds when using the standard Anthropic Messages shape:
{
"system": "You are a helpful assistant.",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "hi"
}
]
}
]
}
So the endpoint, API key, model, and billing plan are working.
The failure only appears when Claude Code sends a request containing messages[].role = "system".
This also occurred with SessionStart hook additional context from plugins. After disabling those plugins, another raw dump still showed messages[1].role = "system" containing the built-in skills list:
The following skills are available for use with the Skill tool:
...
This suggests the issue is not limited to one plugin. It appears to be how Claude Code 2.1.154 serializes session context / skill context / hook additional context into the outgoing Messages API request.
This effectively breaks Claude Code for third-party Anthropic-compatible providers that validate messages[].role according to the traditional Messages API schema.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗