Error reference¶
Kind: reference. This page maps domain failures, adapter behavior and retry
boundaries for typed generation. Sister shape: judgevet
docs/reference/errors.md.
Status: current code. A future error-hierarchy ADR (#43) may refine names or grouping; until then, treat the types below as authoritative.
Parent: #29.
Domain vs adapter errors¶
Domain errors describe typed generation and schema compilation inside
typevet.domain. They do not perform HTTP. Callers use them to classify bad
input, unsupported schema shapes and invalid model output.
Adapter errors are domain exception types raised from outbound adapters
(FakeGenerationAdapter, the llama.cpp adapters and the vLLM adapters).
Adapters translate httpx transport failures into TransportError, llama.cpp
and vLLM HTTP error statuses into BackendHttpError, other parse or shape
failures into GenerationError, and run jsonschema validation into
SchemaValidationError. The candidate scoring adapters raise
ScoringValidationError and ScoringUnsupportedCapabilityError, which
subclass GenerationError through ScoringError. They do not define a
parallel adapter-specific hierarchy.
Schema compilation uses SchemaError (ValueError subclass) from
typevet.domain when a JSON Schema mapping cannot be compiled. That happens
before any generation port call. It is not a GenerationError and is never
raised from GenerationPort.generate.
The generation port documents failure
modes every implementation may raise: TransportError, BackendHttpError,
other GenerationError parse failures, and fail-fast SchemaValidationError.
Generation failures¶
The package root typevet exports only the first four types.
typevet.domain.errors exports all of them:
| Type | Parent | Meaning |
|---|---|---|
GenerationError |
Exception |
A generation call failed before a valid GenerationResult existed (parse, shape, or empty fake). |
TransportError |
GenerationError |
The HTTP client failed before a usable response (status_code and body_snippet are None). |
BackendBusyError |
BackendHttpError |
With TYPEVET_LLAMA__AUTOLOAD=false, the llama.cpp router answered HTTP 400 model is not loaded and another model holds the last router slot. typevet did not load the model. See llama.cpp autoload. |
BackendHttpError |
GenerationError |
llama.cpp or vLLM returned HTTP status 400 or above; carries status_code and truncated body_snippet (500 chars max). From vLLM it also carries retry_after_seconds and rate_limit; see retry boundaries. |
SchemaValidationError |
GenerationError |
Parsed output failed JSON Schema validation. Optional payload holds the rejected value. |
GenerationUnsupportedCapabilityError |
GenerationError |
The adapter cannot send a part of the request, such as images. Raised before any HTTP call. Import from typevet.domain or typevet.domain.errors. |
ScoringError |
GenerationError |
A candidate scoring call failed before a valid result existed. |
ScoringValidationError |
ScoringError |
Scores failed the coverage or finiteness rules. |
ScoringUnsupportedCapabilityError |
ScoringError |
The backend cannot honor the requested score stage or capability. |
SchemaValidationError stores the message in standard exception args. When
set, payload is the parsed object or mapping that failed validation (see
domain/errors.py).
Request validation (not generation errors)¶
GenerationRequest rejects bad asks in __post_init__ with ordinary Python
errors, not GenerationError:
| Condition | Type |
|---|---|
Blank prompt or model |
ValueError |
schema is not a mapping |
TypeError |
schema.type is set and not "object" |
ValueError |
A media item is not an ImageInput |
TypeError |
Count of MEDIA_MARKER in prompt differs from len(media) |
ValueError |
schema fails the JSON Schema Draft 2020-12 meta-schema, or json.dumps cannot encode it (adapter check) |
ValueError (schema is not a valid JSON Schema:) |
The last row comes from check_request_schema in
chat_completion.py.
The llama.cpp and vLLM generation adapters, sync and async, call it before the
request, so no request is sent. The check runs once for each distinct schema.
CandidateScoringRequest also checks its fields in __post_init__
(candidate_scoring_request.py):
| Condition | Type | Message |
|---|---|---|
A media item is not an ImageInput |
TypeError |
media[N] must be ImageInput, got TYPE |
Blank model, empty prefix, no candidates, a duplicate label or token-id sequence, or a MEDIA_MARKER count that differs from len(media) |
ScoringValidationError |
Names the failed rule |
The marker-count message names both counts and does not quote the prefix.
The judgment adapter neutralizes a <__media__> in caller text (#433). A
state, instructions or criteria that hold the marker do not raise this error.
Fix the request; do not treat these as retryable generation failures.
Event loop misuse (not a generation error)¶
An async vLLM adapter that owns its HTTP client serves only the first event
loop that calls generate. This applies to the adapter from
async_vllm_generation_adapter and to AsyncVllmGenerationAdapter built with
client=None. A call on a different loop raises a plain RuntimeError
before any request:
| Condition | Type | Message |
|---|---|---|
generate runs on a different event loop from the first call |
RuntimeError |
build one adapter per event loop |
This RuntimeError is not a GenerationError. A caller that catches only
GenerationError does not catch it. The failure is permanent for that
adapter, so do not retry it. Build one adapter for each event loop. An adapter
with an injected client does not do this check.
Schema compilation failures¶
Import SchemaError from typevet.domain (not the package root):
| Type | Parent | Meaning |
|---|---|---|
SchemaError |
ValueError |
The JSON Schema mapping is outside the supported compile subset (compile_json_schema, Decision, dependency layers). |
compile_json_schema and helpers in
decision_compile.py raise
SchemaError with a human-readable message for unsupported keywords, bad
enums, dependency cycles and similar compile-time rules.
For some unsupported field types without a finite enum, compilation raises
NotImplementedError instead of SchemaError. That signals a missing feature,
not a malformed document the caller can correct by editing one field.
llama.cpp adapter mapping¶
LlamaCppGenerationAdapter
POSTs to v1/chat/completions with response_format json_schema. HTTP status
400 and above are treated as adapter failure (constant _HTTP_ERROR_STATUS).
| Condition | Raised type | Typical message prefix |
|---|---|---|
Request has media (images) |
GenerationUnsupportedCapabilityError |
llama.cpp generation adapters do not send images (no HTTP call) |
httpx.HTTPError on POST |
TransportError |
llama.cpp request failed: |
| HTTP status ≥ 400 | BackendHttpError |
llama.cpp HTTP {status}: (body_snippet truncated to 500 chars) |
autoload off, HTTP 400 model is not loaded, and another model holds the last router slot, a /models or /props read fails, or the one retry with autoload=true answers the same 400 |
BackendBusyError |
llama.cpp HTTP 400: model is not loaded |
| Response body is not JSON | GenerationError |
llama.cpp returned non-JSON HTTP body |
Missing or empty choices[0].message.content |
GenerationError |
llama.cpp response missing… or empty message content |
| Message content is not valid JSON | GenerationError |
model content was not valid JSON |
| Parsed JSON root is not an object | SchemaValidationError |
model JSON root must be an object (payload set) |
Parsed object holds a non-finite number (NaN, Infinity) |
SchemaValidationError |
structured output contains a non-finite number (payload set) |
jsonschema.validate fails on parsed object |
SchemaValidationError |
output failed schema: (payload set) |
The /tokenize call of the Gemma 4 native vision factory uses the same HTTP
mapping. A failure before a response raises TransportError. A status of 400
or above raises BackendHttpError
(#298). A 200 body that
is not JSON raises GenerationError with the prefix
llama.cpp returned non-JSON HTTP body. A JSON body without a tokens list of
integers raises GenerationError with the prefix
llama.cpp /tokenize response missing a tokens list
(#310).
The /apply-template probe of the same factory runs when a session opens and
uses the same HTTP mapping. A failure before a response raises
TransportError. A status of 400 or above raises BackendHttpError. A 200 body
that is not JSON raises GenerationError with the prefix
llama.cpp returned non-JSON HTTP body. A JSON body without a prompt string
raises GenerationError with the prefix
llama.cpp /apply-template response missing a prompt string
(#311).
The llama.cpp scoring calls /tokenize, /props and /completion with
n_predict: 0 change no server state. The /apply-template probe also
changes no server state and uses the same retry. The router can read a request on a
connection (new or reused) and then close it with no response head. httpx then raises
RemoteProtocolError ("Server disconnected without sending a response").
These calls then send the request one more time on a new connection. A second close, a timeout, a malformed response and an error
status do not cause a retry. They raise TransportError or BackendHttpError
as the table shows
(#305).
This table describes local mapping only. It does not assert which HTTP statuses a given llama.cpp build returns for every failure mode. See Run Gemma 4 on llama.cpp for the live path.
vLLM adapter mapping¶
VllmGenerationAdapter
and AsyncVllmGenerationAdapter POST to v1/chat/completions with
structured_outputs json. Each call makes one POST and no retry.
vllm/http_mapping.py
maps the HTTP outcome. HTTP status 400 and above is an adapter failure
(HTTP_ERROR_STATUS in adapters/outbound/http_errors.py).
| Condition | Raised type | Typical message prefix |
|---|---|---|
Request schema is not a valid JSON Schema |
ValueError |
schema is not a valid JSON Schema: (no HTTP call) |
httpx.HTTPError on POST, including an httpx timeout |
TransportError |
vLLM request failed: |
| HTTP status ≥ 400 | BackendHttpError |
vLLM HTTP {status}: (body_snippet truncated to 500 chars; empty with HTML body withheld when the body is HTML) |
| HTTP status 3xx | BackendHttpError |
vLLM HTTP {status}: redirect not followed (no Location value) |
| Response body is not JSON | GenerationError |
vLLM returned non-JSON HTTP body |
Missing or empty choices[0].message.content |
GenerationError |
vLLM response missing… or vLLM returned empty message content |
| Message content is not valid JSON | GenerationError |
model content was not valid JSON |
| Parsed JSON root is not an object | SchemaValidationError |
model JSON root must be an object (payload set) |
Parsed object holds a non-finite number (NaN, Infinity) |
SchemaValidationError |
structured output contains a non-finite number (payload set) |
jsonschema.validate fails on parsed object |
SchemaValidationError |
output failed schema: (payload set) |
| Async adapter with an owned client runs on a second event loop | RuntimeError |
build one adapter per event loop (no HTTP call) |
A generation request with images is not refused. The adapter sends each image
as an image_url block.
VllmCandidateScoringAdapter
uses the same HTTP mapping for transport errors, status 400 and above, and a
body that is not JSON. It then reads the logprobs:
| Condition | Raised type | Typical message prefix |
|---|---|---|
More than 128 candidates, a stage other than PRE_SAMPLING, or a candidate with more than one token id |
ScoringUnsupportedCapabilityError |
vLLM accepts at most… or VllmCandidateScoringAdapter supports… (no HTTP call) |
| Response root is not an object | GenerationError |
vLLM chat completions response root must be an object |
Missing choices[0].logprobs.content[0].top_logprobs |
GenerationError |
vLLM response missing choices[0].logprobs.content[0].top_logprobs |
top_logprobs is not a list |
GenerationError |
vLLM top_logprobs must be a list |
An entry has no token or logprob, or its logprob is not a real number |
GenerationError |
vLLM top_logprobs entry must be… or vLLM top_logprobs logprob … is not a real number |
An entry token is not in token_id:N form |
GenerationError |
vLLM top_logprobs token … is not in token_id:N form |
| Two entries have the same token id | ScoringValidationError |
vLLM top_logprobs holds a duplicate entry |
top_logprobs has no score for a requested candidate |
ScoringValidationError |
missing scores for requested candidates: |
A candidate logprob is not finite, or is positive above 1e-6 |
ScoringValidationError |
non-finite logprob for candidate… or positive logprob for candidate… |
The /tokenize call of the vLLM judgment factory uses the same HTTP mapping.
A reply without a list of integer tokens raises GenerationError
(vLLM /tokenize response must hold a list of integer tokens).
This table describes local mapping only. It does not assert which HTTP statuses a given vLLM build returns for every failure mode.
Fake adapter mapping¶
FakeGenerationAdapter validates
a fixed or callable mapping with the same jsonschema path as llama.cpp:
| Condition | Raised type |
|---|---|
Constructor given no value, responder, or fail |
ValueError |
fail= configured |
Raises that exception as-is (tests use GenerationError) |
| No value source at generate time | GenerationError |
| Value fails schema | SchemaValidationError (fake output failed schema:) |
Retry and exception boundaries¶
typevet does not expose a retryable flag or a built-in retry loop on
generation adapters. Each generate call performs at most one HTTP round trip
(llama.cpp or vLLM) or one validation pass (fake). The llama.cpp scoring calls
have one narrow retry. It covers a close before a complete status line and
headers, on a new or reused connection (see llama.cpp adapter mapping).
Use these boundaries when a caller adds retries:
| Failure | Retry at generation layer? | Notes |
|---|---|---|
SchemaValidationError |
No (fail-fast) | Same prompt and schema may repeat the same invalid output; fix schema, prompt, or model. Adapter docstring: fail-fast on schema mismatch. |
TransportError, BackendHttpError (5xx) |
Optional caller policy | Not implemented in-repo; a supervisor may retry with backoff outside the adapter. |
BackendHttpError (4xx) |
Usually no | Router config, model id, or request the backend rejects. A gateway 429 is this error too. Read retry_after_seconds and rate_limit before a retry. |
BackendHttpError (3xx) |
No | The vLLM clients do not follow redirects. The error carries the 3xx status and no Location value. |
GenerationError (bad JSON shape on 2xx) |
Usually no | Non-recoverable response shape from the model or router. |
GenerationUnsupportedCapabilityError |
No | Use an adapter that supports the request, or remove the images. |
SchemaError, NotImplementedError |
No | Fix or narrow the schema before calling generation. |
GenerationRequest ValueError / TypeError |
No | Fix the request object. |
RuntimeError (build one adapter per event loop) |
No | Build one adapter for each event loop. |
Retry hints on a vLLM error¶
A vLLM BackendHttpError holds two retry hints from the response.
typevet reads them and makes no retry.
| Attribute | Value |
|---|---|
retry_after_seconds |
float or None. Retry-After as delta-seconds gives that number. A wait above one year (31,536,000 seconds) gives None. |
rate_limit |
Read-only mapping of the x-ratelimit-* and ratelimit-* headers. Names are lowercase. Values are verbatim, except the masked key and header values. |
An HTTP-date in Retry-After gives the seconds from the response Date header to that date.
Without a valid Date header, the count starts at the current UTC time.
A date in the past gives 0.0.
Any other value, or two Retry-After fields, gives None.
rate_limit never holds a header name that the request sent, or Authorization.
Thus the auth header and the TYPEVET_VLLM__HEADERS names stay out.
A value can hold the API key or a TYPEVET_VLLM__HEADERS value.
Then that token shows as ***, by the whole-token rule.
A llama.cpp error has retry_after_seconds None and an empty rate_limit.
Request id on a vLLM error¶
A vLLM BackendHttpError and a vLLM TransportError hold request_id (#356).
| Attribute | Value |
|---|---|
request_id |
str or None. The TYPEVET_VLLM__REQUEST_ID_HEADER value of the request that failed. |
The value is None when no request-id header is configured.
A redirect and an HTML error body also give the id.
A /tokenize failure gives the id of that /tokenize request.
The id is not a secret, so the error does not mask it.
No error message holds the id, and typevet never logs it.
A llama.cpp error has request_id None.
except GenerationError catches SchemaValidationError because it subclasses
GenerationError. Use except SchemaValidationError when validation failures
need distinct handling (for example logging payload).
Each backend has its own mapping module:
llama_cpp/http_mapping.py
and
vllm/http_mapping.py.
Both use the status and snippet limits in
http_errors.py.
Adapters map httpx.HTTPError to TransportError and HTTP status ≥ 400 to
BackendHttpError. Other library or application errors propagate unless the
caller handles them.
The following example runs offline and prints nothing when its assertions pass. It demonstrates types only; it makes no network call.
from typevet import BackendHttpError, GenerationError, SchemaValidationError
from typevet.domain import SchemaError, compile_json_schema
assert issubclass(SchemaValidationError, GenerationError)
assert issubclass(BackendHttpError, GenerationError)
err = SchemaValidationError("missing key", payload={"x": 1})
assert err.payload == {"x": 1}
http_err = BackendHttpError(
"llama.cpp HTTP 500: oops", status_code=500, body_snippet="oops"
)
assert http_err.status_code == 500
assert http_err.body_snippet == "oops"
try:
compile_json_schema({"type": "array"})
except SchemaError:
pass
else:
raise AssertionError("expected SchemaError")
assert not issubclass(SchemaError, GenerationError)
Public surface¶
| Symbol | Import from |
|---|---|
GenerationError, TransportError, BackendHttpError, SchemaValidationError |
typevet or typevet.domain.errors |
SchemaError, compile_json_schema, Decision |
typevet.domain |
GenerationPort |
typevet.ports.generation |
ScoringError, ScoringValidationError, ScoringUnsupportedCapabilityError |
typevet.domain or typevet.domain.errors |
LlamaCppGenerationAdapter, FakeGenerationAdapter |
typevet.adapters.outbound |
VllmGenerationAdapter, AsyncVllmGenerationAdapter, VllmCandidateScoringAdapter |
typevet.adapters.outbound |
Do not document exception types that are not raised by the current tree. When #43 lands, revise this page to match the accepted hierarchy.