Skip to content

Claude Code mid-conversation system messages break OpenAI routing #3608

@dongqihouse

Description

@dongqihouse

Is it a request payload issue?
[x] Yes, this is a request payload issue. I am using a client/cURL to send a request payload, but I received an unexpected error.
[ ] No, it's another issue.

If it's a request payload issue, you MUST know
The issue is triggered by the latest Claude Code client sending Anthropic's mid-conversation-system-2026-04-07 beta. Compared with older Claude Code requests, the new request includes:

Anthropic-Beta: claude-code-20250219, interleaved-thinking-2025-05-14, mid-conversation-system-2026-04-07, effort-2025-11-24

When Claude Code routes through CLIProxyAPI to an OpenAI/OpenAI-compatible model, the translated OpenAI Chat Completions payload can contain messages[].role == "system" in the middle of the conversation.

Describe the bug
Claude Code's new Mid-conversation system messages feature is not compatible with the current Claude-to-OpenAI translator. Mid-conversation Anthropic messages with role: "system" are passed through as OpenAI Chat Completions system messages. Some OpenAI/OpenAI-compatible models reject this with:

System messages are not allowed

Top-level Anthropic system should keep the existing behavior, but messages[] entries with role: "system" need to be converted to a role accepted by OpenAI Chat Completions for in-conversation instructions, such as developer.

CLI Type
claude code routed to openai/openai-compatibility

Model Name
OpenAI/OpenAI-compatible models, for example gpt-5 or compatible provider aliases.

LLM Client
Claude Code latest version with the mid-conversation-system-2026-04-07 beta enabled.

Request Information
Observed request difference:

Anthropic-Beta before:
claude-code-20250219, interleaved-thinking-2025-05-14, effort-2025-11-24

Anthropic-Beta after:
claude-code-20250219, interleaved-thinking-2025-05-14, mid-conversation-system-2026-04-07, effort-2025-11-24

Representative Anthropic payload shape:

{
  "model": "claude-*",
  "messages": [
    {"role": "user", "content": [{"type": "text", "text": "first"}]},
    {"role": "system", "content": [{"type": "text", "text": "mid instruction"}]},
    {"role": "user", "content": "continue"}
  ]
}

Current translated OpenAI shape can include a mid-conversation system role:

{
  "messages": [
    {"role": "user", "content": [{"type": "text", "text": "first"}]},
    {"role": "system", "content": [{"type": "text", "text": "mid instruction"}]},
    {"role": "user", "content": "continue"}
  ]
}

Expected behavior
Claude Code requests with mid-conversation system messages should be accepted when routing to OpenAI/OpenAI-compatible models.

Suggested translator behavior:

  • Keep top-level Anthropic system conversion unchanged.
  • Convert only messages[] entries whose role is system to OpenAI developer.
  • Preserve message content unchanged.

Example expected OpenAI shape:

{
  "messages": [
    {"role": "user", "content": [{"type": "text", "text": "first"}]},
    {"role": "developer", "content": [{"type": "text", "text": "mid instruction"}]},
    {"role": "user", "content": "continue"}
  ]
}

Screenshots
N/A

OS Type

  • OS: macOS
  • Version: not OS-specific

Additional context
Probable location:

  • internal/translator/openai/claude/openai_claude_request.go

A minimal fix is to normalize roles while iterating over Anthropic messages[]:

func normalizeClaudeRoleForOpenAI(role string) string {
	if role == "system" {
		return "developer"
	}
	return role
}

Then use the normalized role for message conversion, without changing the existing top-level system handling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions