Files
homelab-frontend/tasks/2.13-anthropic-error-shape.md
T
Story Crater BotandClaude Opus 5 058f11cf2b
CI / Test (push) Canceled after 0s
CI / Vet (push) Canceled after 0s
CI / Build (push) Canceled after 0s
CI / Security (govulncheck) (push) Canceled after 0s
chore: initial commit of Go API gateway
Baseline for the Kong replacement on api.riotpiao.com. Brings the working
tree under version control for the first time: gateway source, the task
board that drives the agent runs, test fixtures, and K8s manifests.

Anchor the gateway ignore rule to the repo root. Unanchored, "gateway"
also matched the cmd/gateway/ source directory, so the program entrypoint
was excluded from every commit.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-19 20:54:34 -07:00

2.6 KiB

2.13 — Anthropic error shape on /llm/* (RED)

Phase: 2 — LLM surfaces Stage: RED Depends on: 2.10, 4.3

/v1/* renders rejections as RFC 9457 application/problem+json. /llm/* must not. The same underlying rejection gets two renderings, chosen purely by which surface received the request. Write the failing tests against both renderings first.

  • Every error response from a /llm/* path has the body {"type":"error","error":{"type":"...","message":"..."}} and Content-Type: application/json
  • No /llm/* response ever carries application/problem+json, and no /v1/* response ever carries the Anthropic error shape
  • An unknown or missing model returns invalid_request_error with a message naming the rejected value and listing the configured model names, derived from the registry rather than hardcoded
  • A request missing the required max_tokens returns invalid_request_error naming max_tokens
  • A body that is not valid JSON returns invalid_request_error with a message distinguishable from the unknown-model case
  • A request for an out-of-scope feature returns invalid_request_error naming the feature, for example tools, images or prompt caching
  • Exhausting the shared slot queue returns HTTP 429 with error type rate_limit_error and a Retry-After header
  • An upstream failure or timeout returns HTTP 5xx with error type api_error, and the message leaks no upstream host, port or internal path
  • No rejection reaches an upstream, and none falls back to a default model
  • Each rejection is logged with its reason and its surface; the request body is never logged
  • The same malformed request sent to /v1/chat/completions and /llm/v1/messages yields the same HTTP status with the two different body shapes

Verify

curl -s -i localhost:8080/llm/v1/messages -H 'content-type: application/json' \
  -d '{"model":"gpt-4","max_tokens":8,"messages":[]}'
# expected: 4xx, content-type application/json, body {"type":"error","error":{"type":"invalid_request_error",...}}
#           listing reasoning, ornith:35b, qwen2.5:3b-instruct

curl -s -i localhost:8080/llm/v1/messages -H 'content-type: application/json' -d 'not json' \
  | grep -i 'content-type'
# expected: application/json, never application/problem+json

curl -s -i localhost:8080/v1/chat/completions -H 'content-type: application/json' \
  -d '{"model":"gpt-4","messages":[]}' | grep -i 'content-type'
# expected: application/problem+json, confirming the two surfaces render differently