Skip to content

Anthropic path 502s when the system field contains a newline (json.RawMessage marshal error) #86

Description

@letuduykhanh

Summary

On the Anthropic-compatible path (/v1/messages — e.g. minimax-* / qwen-* on the opencode-go provider), the proxy returns 502 all models failed whenever the request's top-level system field contains a newline. The upstream request is never sent — the proxy fails to marshal the request first.

Error (debug log)

level=WARN msg="non-retryable error (skipping circuit breaker), trying fallback"
  error="failed to marshal anthropic request: json: error calling MarshalJSON for type json.RawMessage: invalid character '\n' in string literal"
level=ERROR msg="request error" status=502 message="all models failed" error="all models failed (1 attempts)"

Minimal repro

Against the proxy's /v1/messages (Anthropic format), with an Anthropic-path model:

# single-line system → 200 OK
curl -s http://127.0.0.1:PORT/v1/messages \
  -H "anthropic-version: 2023-06-01" -H "content-type: application/json" \
  -d '{"model":"minimax-m3","max_tokens":512,"system":"be terse","messages":[{"role":"user","content":"hi"}]}'

# system WITH a newline → 502 (marshal error above)
curl -s http://127.0.0.1:PORT/v1/messages \
  -H "anthropic-version: 2023-06-01" -H "content-type: application/json" \
  -d '{"model":"minimax-m3","max_tokens":512,"system":"be terse\nreply in one word","messages":[{"role":"user","content":"hi"}]}'

Reproduces on minimax-m3 and qwen3.7-plus. Trigger is purely the newline in system; payload otherwise minimal (no tools, no claude-code involvement).

Suspected root cause

MessageRequest.System is json.RawMessage (pkg/types/anthropic.go, cf. #36). On the Anthropic→Anthropic transform the proxy appears to rebuild that RawMessage from a decoded string without re-JSON-escaping, so a literal \n yields invalid JSON that MarshalJSON rejects.

Impact

  • Deterministic: any Anthropic-path model + a system containing a newline → 502. Every real agent (e.g. Claude Code) sends a multi-line system prompt, so these models fail 100% in practice.
  • OpenAI-path models (deepseek-* / mimo-* / glm-* / kimi-*) are unaffected — different transform path.
  • OpenCode's API is fine: the identical payload sent directly to https://opencode.ai/zen/go/v1/messages (with x-api-key) returns 200. The defect is in the proxy's request transformation, not upstream.

Environment

  • routatic-proxy 0.3.6 (Homebrew, macOS arm64)
  • Models confirmed: minimax-m3, qwen3.7-plus

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions