# 2.13 — Anthropic error shape on `/llm/*` (RED) Phase: 2 — LLM surfaces Stage: RED Depends on: [2.10](2.10-anthropic-request-translation.md), [4.3](4.3-problem-json-errors.md) `/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 ```bash 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 ```