Error Handling
LLMError types, what triggers each, and what gets retried
Failures from call() are surfaced as a normalized LLMError with a specific type. Check
err.type to branch on what happened:
import { isLLMError } from 'vern-llm';
try {
const result = await llm.call({ systemPrompt, userContent, schema });
} catch (err) {
if (isLLMError(err)) {
switch (err.type) {
case 'validation':
console.log(err.issues); // the schema's error object
break;
case 'api':
console.log(err.status); // HTTP status, if the provider returned one
break;
case 'circuit_open':
// provider is down, back off and do not retry immediately
break;
}
}
throw err;
}Error types
timeout
A single attempt exceeded timeoutMs. Retried like any other transient failure.
api
The provider returned an HTTP error with a status code. Retried unless the status is in
nonRetryableStatus (default 400, 401, 403).
parse
The response content was not valid JSON. Never retried because a malformed response will not fix itself on retry.
validation
JSON parsed successfully but failed schema.safeParse. Never retried for the same reason as
parse. Carries .issues with the validator's error object.
circuit_open
The circuit breaker is open because the provider has failed too many times in a row. Fails immediately without hitting the provider.
aborted
The caller's AbortSignal fired. This represents intentional cancellation by the caller, not a
timeout. See Cancellation.
unknown
Anything that does not fit the above categories, including network errors with no status code and unexpected provider responses. Retried by default.
What gets retried
shouldRetry skips a retry when any of these are true:
- The signal has already aborted
error.typeis'parse'or'validation'because these come from the response content rather than a transient fault, so retrying will not helperror.statusis innonRetryableStatus(default[400, 401, 403])
Everything else, including timeouts, 5xx errors, network failures, and unknown errors, is retried up to
maxRetries with exponential backoff and jitter.
Exhausting all retries also records a circuit breaker failure if one is configured and throws the
final normalized LLMError. The raw underlying error is not preserved on the thrown value beyond
what extractStatus could pull from it.