Skip to content

Thirteen failure kinds, each named by what fixes it

OpenAI429 · out of creditAnthropic529 · overloadedPrompt too long400 · 213k > 200kPlan excludes the API403 · entitlementMoonshot400 · 余额不足Socket died mid-flightno status at allGemini429 · retryDelay 48sRetrytime fixes ittimeoutnetworkoverloadrateAccountmoney or plan fixes itquotaentitlementauthContextsending less fixes itcontextOursour request was wrongmodelcontentinvalidInertnothing to doabortedunknown

Wider than this screen. Scroll the plate sideways.

Fig. 1: the real classifier from the package, running in your browser. Click a ray, or paste your own failure below.

Input

Paste a real one. Nothing is sent anywhere. This is the package running locally.

Verdict

quotaaccount: money or plan fixes it

Top up, or wait for the reset. Retrying will not help.

isTransient
false
isBackupEligible
false
retryAfterMs
-
status
429

providerkit

One seam for every LLM provider, plus the failure handling you only learn in production. Not a framework: no loop, no prompts, no graph. Your loop is where your product lives.

Terminal window
bun add @providerkit/core

Get startedRead the taxonomySource

Reading this as an agent? /llms.txt indexes the site, /kinds.json is the taxonomy as data, and appending .md to any page returns its source.

Most “unified LLM interface” libraries stop at the interface. That is not where the time goes. This is where the time goes.

The stream that never ends

A provider accepts the request and then sends nothing. Every SDK’s default is to wait forever, and the symptom is the worst kind: none at all.

The socket four levels down

A dead connection carries no HTTP status, and the useful code sits on cause.cause.cause. Miss it and every network blip reads as permanent.

One cause, five statuses

Running out of credit arrives as 429, 402, 403 and 400 depending on the vendor. Status alone tells you to retry an empty balance.

The 429 that is really a full prompt

Waiting fixes a throttle and does nothing for an overflowing context window. They arrive under the same status.

Cache tokens, counted twice

Anthropic reports cache reads outside the input count; OpenAI reports them inside it. Hand both through unreconciled and the same conversation costs two different things.

The answer that was already there

A turn cut off at its output ceiling leaves invalid JSON, and a run reports nothing while the answer sits in the fragments.

One provider interface, one error type, and the handful of primitives a loop actually needs underneath it.

import { createAnthropicProvider } from "@providerkit/core";
const provider = createAnthropicProvider({
apiKey: process.env.ANTHROPIC_API_KEY,
model: "claude-sonnet-5",
});
for await (const chunk of provider.createStream(messages, tools, { effort: "medium" })) {
if (chunk.content) write(chunk.content);
// one usage shape across every provider
if (chunk.usage) record(chunk.usage);
}
import { ProviderError, isTransient, isBackupEligible } from "@providerkit/core";
try {
/* ... */
} catch (raw) {
const err = ProviderError.from("anthropic", raw);
if (err.kind === "context") return compactAndRetry();
if (isBackupEligible(err.kind)) return otherModel();
if (isTransient(err.kind)) return retry(err.retryAfterMs);
surface(err); // .body has their actual words
}

providerkit was extracted from five production codebases that had each independently grown the same layer: about 9,100 lines solving one ~2,000-line problem. They had three separate 60-second idle watchdogs, identical down to the constant. On one day in September 2026, two of them shipped the same five fixes independently.

They had also each learned a different part of it. One walked the cause chain for dead sockets. One parsed Gemini’s RetryInfo. One read the body before the status and knew the quota wordings in five languages. One knew Anthropic’s 529 and when a failure is worth a different model. One could rescue an answer from a tool call the model truncated.

5codebases pooled
13failure kinds, by their fix
416tests, every failure any of them saw
0runtime dependencies

The classifier on this page is the union of all five, and the test suite is every failure any of them ever saw. That is the part worth having.

Four of the five run their AI on it in production today. The migrations that put it there were net-negative every time: −452, −1,621 and −197 lines, and the last one trades three vendor SDKs for this package.