claude-code - 💡(How to fix) Fix [BUG] 2.1.154 appears to send messages[].role="system" to Anthropic-compatible providers, causing schema parse failures

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

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.

Code Example

{
  "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: ..."
    }
  ]
}

---

{
  "role": "system",
  "content": "SessionStart hook additional context: ..."
}

---

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.

---

$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"

---

$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"

---

API Error: 400 Failed to deserialize the JSON body into the target type:
messages[1].role: unknown variant `system`, expected `user` or `assistant`

---

$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"

---

$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])
      }
  ]
}'

---

{
  "message_roles": ["user", "system"],
  "system_messages_in_messages": [
    {
      "index": 1,
      "content_preview": "The following skills are available for use with the Skill tool: ..."
    }
  ]
}

---

{
  "system": "You are a helpful assistant.",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "hi"
        }
      ]
    }
  ]
}

---

The following skills are available for use with the Skill tool:
...
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • 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:

{
  "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:

  1. Keep all system/context/skill/session-start content in the top-level system field, or
  2. Provide a compatibility flag that serializes system reminders as user-side <system-reminder> text, or
  3. 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

  1. Use Claude Code 2.1.154 on Windows native.

  2. 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"
  1. 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"
  1. 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`
  1. 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"
  1. 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])
      }
  ]
}'
  1. 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.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING