Skip to content

/v1/messages rejects system-role messages (422) — Claude Code can't use exo as ANTHROPIC_BASE_URL #2193

Description

@batmunkh0612

Environment

  • exo official app 1.0.71 (also present in main @ b5375f8c: src/exo/api/types/claude_api.pyClaudeRole = Literal["user", "assistant"])
  • Client: Claude Code 2.1.169 pointed at exo via ANTHROPIC_BASE_URL=http://<node>:52415

Symptom

Claude Code fails on its first request:

API Error: 422 {"detail":[{"type":"literal_error","loc":["body","messages",1,"role"],
"msg":"Input should be 'user' or 'assistant'","input":"system", ...}]}

Minimal repro without Claude Code:

curl -s http://localhost:52415/v1/messages \
  -H 'Content-Type: application/json' -H 'anthropic-version: 2023-06-01' \
  -d '{"model":"<loaded-model>","max_tokens":32,
       "messages":[{"role":"user","content":"hi"},{"role":"system","content":"be brief"}]}'

Discussion

Strictly, Anthropic's Messages API only allows user/assistant in messages (system prompt goes in the top-level system field). In practice, though, real-world Anthropic-format clients — including Claude Code, the most common reason people set ANTHROPIC_BASE_URL to an exo node — do send system-role entries inside messages, so exo's strict Literal["user","assistant"] makes the endpoint unusable for exactly the clients it exists for.

A lenient adapter behavior would make /v1/messages practically useful: accept "system" in ClaudeRole and fold such messages into the system prompt (prepend to the top-level system value) before conversion, as other Anthropic-compatible servers do.

Happy to send a PR if this direction is acceptable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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