Inference APIs return a small, stable set of failures, and most integrations handle them with a blanket retry that makes two of them worse and hides a third. Knowing which is which takes about ten minutes and saves an outage.

The shape of an error

Both major dialects return a JSON body with a structured error object alongside the HTTP status. In the OpenAI dialect it is {"error": {"message", "type", "param", "code"}}; Anthropic returns {"type": "error", "error": {"type", "message"}}. The status tells you the class; the type or code field tells you what to do, and it is the field most client code discards. Log both, and log the request id header — every provider issues one, and it is the only thing a support conversation can proceed from.

The distinction that organises everything below is not client-versus-server, which is what the status code nominally encodes. It is will the identical request succeed later? Three answers exist: yes after a wait (capacity and rate conditions), no until something changes in the request (validation, auth, model identity), and no until something changes outside the request entirely (a billing state, a retired snapshot, a regional restriction). Only the first is retryable, the second belongs in an alert on your own deploy, and the third needs a human. Several genuinely different conditions share a status code across that boundary, which is why classifying on status alone produces a retry policy that is wrong in both directions — hammering a wall in one place and giving up on a transient blip in another.