[GH-ISSUE #639] Anthropic endpoint rejects Opus 4.8 mid-conversation system messages #188

Closed
opened 2026-10-26 09:54:54 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @DT-HJ on GitHub (May 29, 2026).
Original GitHub issue: https://github.com/deepseek-ai/awesome-deepseek-integration/issues/639

Summary

Anthropic added mid-conversation system messages with Claude Opus 4.8. The Anthropic Messages API can now include role: "system" entries inside the messages[] array, not only in the top-level system field. This is used to update instructions mid-conversation without invalidating earlier prompt-cache content.

Claude Code appears to use this newer message shape during normal interactive sessions. DeepSeek's Anthropic-compatible endpoint still rejects messages[] entries with role: "system", causing HTTP 400 errors:

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

This makes current Claude Code + DeepSeek Anthropic endpoint usage unreliable.

Relevant Anthropic documentation

My understanding from the docs is that messages[].role = "system" is now part of the Anthropic-compatible surface, especially for preserving prompt-cache behavior while injecting updated instructions during a long task.

Environment

  • Client: Claude Code
  • Observed Claude Code versions: 2.1.154 in session logs, local binary currently 2.1.156
  • Base URL: https://api.deepseek.com/anthropic
  • Model: deepseek-v4-flash
  • Date observed: 2026-05-29
  • Platform: macOS

Environment variables used by Claude Code:

export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
export ANTHROPIC_AUTH_TOKEN=...
export ANTHROPIC_MODEL=deepseek-v4-flash
export ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-v4-pro
export ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-v4-flash
export ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash

Minimal reproduction

A system prompt at the top-level system field works:

curl -sS https://api.deepseek.com/anthropic/v1/messages \
  -H "Authorization: Bearer $DEEPSEEK_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model":"deepseek-v4-flash",
    "max_tokens":16,
    "system":"You are concise.",
    "messages":[{"role":"user","content":"Say OK."}]
  }'

Observed result: HTTP 200.

But a system role inside messages[] fails:

curl -sS https://api.deepseek.com/anthropic/v1/messages \
  -H "Authorization: Bearer $DEEPSEEK_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model":"deepseek-v4-flash",
    "max_tokens":16,
    "messages":[
      {"role":"system","content":"You are concise."},
      {"role":"user","content":"Say OK."}
    ]
  }'

Observed result:

{
  "error": {
    "message": "Failed to deserialize the JSON body into the target type: messages[0].role: unknown variant `system`, expected `user` or `assistant` at line 1 column 73",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_request_error"
  }
}

HTTP status: 400.

Claude Code session evidence

In real Claude Code sessions using DeepSeek, these synthetic API errors were recorded:

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

The second case happened after several tool-use turns, so the system role can appear later in messages[], not only at index 0.

Expected behavior

For current Claude Code compatibility, it would be helpful if DeepSeek's Anthropic endpoint supported messages[] entries with role: "system" according to Anthropic's newer mid-conversation system message behavior.

Possible implementation options:

  1. Support role: "system" inside messages[] natively, preserving the intended mid-conversation semantics.
  2. If full semantic support is not available yet, fold those entries into the effective system prompt as a compatibility fallback.
  3. If unsupported by design, document this as a known Claude Code / Opus 4.8 compatibility limitation in the Anthropic API guide.

Local workaround

A local pass-through proxy that folds messages[].role = "system" entries into the top-level system field avoids the HTTP 400. However, this is only a compatibility workaround: it prevents request failure, but it does not perfectly reproduce Anthropic's prompt-cache-preserving mid-conversation semantics.

DeepSeek-side native support would be the correct long-term fix.

Originally created by @DT-HJ on GitHub (May 29, 2026). Original GitHub issue: https://github.com/deepseek-ai/awesome-deepseek-integration/issues/639 ## Summary Anthropic added **mid-conversation system messages** with Claude Opus 4.8. The Anthropic Messages API can now include `role: "system"` entries inside the `messages[]` array, not only in the top-level `system` field. This is used to update instructions mid-conversation without invalidating earlier prompt-cache content. Claude Code appears to use this newer message shape during normal interactive sessions. DeepSeek's Anthropic-compatible endpoint still rejects `messages[]` entries with `role: "system"`, causing HTTP 400 errors: ```text Failed to deserialize the JSON body into the target type: messages[7].role: unknown variant `system`, expected `user` or `assistant` ``` This makes current Claude Code + DeepSeek Anthropic endpoint usage unreliable. ## Relevant Anthropic documentation - Claude Opus 4.8 new feature notes: https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-8 - Mid-conversation system messages: https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages - Prompt caching: https://platform.claude.com/docs/en/build-with-claude/prompt-caching - Claude Opus 4.8 announcement: https://www.anthropic.com/news/claude-opus-4-8 My understanding from the docs is that `messages[].role = "system"` is now part of the Anthropic-compatible surface, especially for preserving prompt-cache behavior while injecting updated instructions during a long task. ## Environment - Client: Claude Code - Observed Claude Code versions: `2.1.154` in session logs, local binary currently `2.1.156` - Base URL: `https://api.deepseek.com/anthropic` - Model: `deepseek-v4-flash` - Date observed: 2026-05-29 - Platform: macOS Environment variables used by Claude Code: ```bash export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic export ANTHROPIC_AUTH_TOKEN=... export ANTHROPIC_MODEL=deepseek-v4-flash export ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-v4-pro export ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-v4-flash export ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash ``` ## Minimal reproduction A `system` prompt at the top-level `system` field works: ```bash curl -sS https://api.deepseek.com/anthropic/v1/messages \ -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ -H 'Content-Type: application/json' \ -H 'anthropic-version: 2023-06-01' \ -d '{ "model":"deepseek-v4-flash", "max_tokens":16, "system":"You are concise.", "messages":[{"role":"user","content":"Say OK."}] }' ``` Observed result: HTTP 200. But a `system` role inside `messages[]` fails: ```bash curl -sS https://api.deepseek.com/anthropic/v1/messages \ -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ -H 'Content-Type: application/json' \ -H 'anthropic-version: 2023-06-01' \ -d '{ "model":"deepseek-v4-flash", "max_tokens":16, "messages":[ {"role":"system","content":"You are concise."}, {"role":"user","content":"Say OK."} ] }' ``` Observed result: ```json { "error": { "message": "Failed to deserialize the JSON body into the target type: messages[0].role: unknown variant `system`, expected `user` or `assistant` at line 1 column 73", "type": "invalid_request_error", "param": null, "code": "invalid_request_error" } } ``` HTTP status: `400`. ## Claude Code session evidence In real Claude Code sessions using DeepSeek, these synthetic API errors were recorded: ```text API Error: 400 Failed to deserialize the JSON body into the target type: messages[1].role: unknown variant `system`, expected `user` or `assistant` at line 1 column 69386 ``` ```text API Error: 400 Failed to deserialize the JSON body into the target type: messages[7].role: unknown variant `system`, expected `user` or `assistant` at line 1 column 113402 ``` The second case happened after several tool-use turns, so the `system` role can appear later in `messages[]`, not only at index 0. ## Expected behavior For current Claude Code compatibility, it would be helpful if DeepSeek's Anthropic endpoint supported `messages[]` entries with `role: "system"` according to Anthropic's newer mid-conversation system message behavior. Possible implementation options: 1. Support `role: "system"` inside `messages[]` natively, preserving the intended mid-conversation semantics. 2. If full semantic support is not available yet, fold those entries into the effective system prompt as a compatibility fallback. 3. If unsupported by design, document this as a known Claude Code / Opus 4.8 compatibility limitation in the Anthropic API guide. ## Local workaround A local pass-through proxy that folds `messages[].role = "system"` entries into the top-level `system` field avoids the HTTP 400. However, this is only a compatibility workaround: it prevents request failure, but it does not perfectly reproduce Anthropic's prompt-cache-preserving mid-conversation semantics. DeepSeek-side native support would be the correct long-term fix.
Author
Owner

@SimZhou commented on GitHub (May 29, 2026):

I can confirm the same compatibility issue from another Claude Code + DeepSeek setup.

Environment:

  • Client: Claude Code v2.1.154
  • Base URL: https://api.deepseek.com/anthropic
  • Model observed: deepseek-v4-pro[1m]
  • Error:
messages[1].role: unknown variant `system`, expected `user` or `assistant`

Additional signal:

  • The same Claude Code setup works through a GLM Anthropic-compatible endpoint, so this does not look like a local Claude Code configuration, API key, billing, or network issue.
  • The failure appears specifically tied to DeepSeek's Anthropic-compatible schema rejecting messages[].role = "system".
  • A temporary workaround reported by users is downgrading Claude Code to 2.1.153 and disabling auto-update, but that is only a short-term mitigation.

Suggested compatibility target:

DeepSeek's /anthropic/v1/messages endpoint should accept Anthropic's newer mid-conversation system messages, or at least normalize those entries into the effective system prompt as a compatibility fallback.

This is becoming visible with Claude Code v2.1.154+, likely because newer Claude Code / Opus 4.8 behavior can emit role: "system" entries inside messages[] after the initial user message.

<!-- gh-comment-id:4569991770 --> @SimZhou commented on GitHub (May 29, 2026): I can confirm the same compatibility issue from another Claude Code + DeepSeek setup. Environment: - Client: Claude Code `v2.1.154` - Base URL: `https://api.deepseek.com/anthropic` - Model observed: `deepseek-v4-pro[1m]` - Error: ```text messages[1].role: unknown variant `system`, expected `user` or `assistant` ``` Additional signal: - The same Claude Code setup works through a GLM Anthropic-compatible endpoint, so this does not look like a local Claude Code configuration, API key, billing, or network issue. - The failure appears specifically tied to DeepSeek's Anthropic-compatible schema rejecting `messages[].role = "system"`. - A temporary workaround reported by users is downgrading Claude Code to `2.1.153` and disabling auto-update, but that is only a short-term mitigation. Suggested compatibility target: DeepSeek's `/anthropic/v1/messages` endpoint should accept Anthropic's newer mid-conversation system messages, or at least normalize those entries into the effective system prompt as a compatibility fallback. This is becoming visible with Claude Code `v2.1.154+`, likely because newer Claude Code / Opus 4.8 behavior can emit `role: "system"` entries inside `messages[]` after the initial user message.
Author
Owner

@DT-HJ commented on GitHub (May 31, 2026):

Update: resolved on DeepSeek's side (empirically verified 2026-05-31)

DeepSeek's Anthropic-compatible endpoint now accepts messages[].role = "system" entries. Re-tested today against https://api.deepseek.com/anthropic/v1/messages:

  • Simple case (one mid-conversation system between turns): HTTP 200
  • Complex case (multiple role: "system" entries, including right after the first user message, plus cache_control blocks): HTTP 200
  • The previous unknown variant 'system', expected 'user' or 'assistant' 400 no longer occurs.

Prompt caching also behaves correctly with the entries left in place: appending a new mid-conversation system at the tail keeps the leading prefix cached (cache-read hit ~99.5%), so there's no full-context cache reset.

So neither a local proxy nor downgrading Claude Code is needed anymore — it works directly via the Anthropic endpoint. Thanks @SimZhou for confirming the original report.

Closing as resolved. (Based on live API behavior as of 2026-05-31; if anyone still hits the 400, it may be a gradual rollout — please comment.)

<!-- gh-comment-id:4587041138 --> @DT-HJ commented on GitHub (May 31, 2026): **Update: resolved on DeepSeek's side (empirically verified 2026-05-31)** DeepSeek's Anthropic-compatible endpoint now accepts `messages[].role = "system"` entries. Re-tested today against `https://api.deepseek.com/anthropic/v1/messages`: - Simple case (one mid-conversation `system` between turns): **HTTP 200** - Complex case (multiple `role: "system"` entries, including right after the first user message, plus `cache_control` blocks): **HTTP 200** - The previous `unknown variant 'system', expected 'user' or 'assistant'` 400 no longer occurs. Prompt caching also behaves correctly with the entries left in place: appending a new mid-conversation `system` at the tail keeps the leading prefix cached (cache-read hit ~99.5%), so there's no full-context cache reset. So neither a local proxy nor downgrading Claude Code is needed anymore — it works directly via the Anthropic endpoint. Thanks @SimZhou for confirming the original report. Closing as resolved. (Based on live API behavior as of 2026-05-31; if anyone still hits the 400, it may be a gradual rollout — please comment.)
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
deepseek-ai/awesome-deepseek-integration#188
No description provided.