Errors¶
lm15.errors — Canonical error taxonomy.
Every provider maps its idiosyncratic error shapes onto this hierarchy. Error classes are the primary signal; canonical string codes exist for serialization and wire formats.
Hierarchy
LM15Error ├── TransportError (network/connection failures at the LM layer) ├── LockTimeoutError (the credential-file lock could not be taken; local, transient) ├── StreamAssemblyError (a stream cannot become a Response without inventing a fact; MAP-9) ├── ConfigurationError (local SDK/configuration failures) │ ├── NotConfiguredError (no API key or required provider config) │ ├── UnknownModelError (the router: a model string that routes nowhere) │ └── AmbiguousModelError (the router: a catalog match under more than one provider) ├── CapabilityError (local provider-adapter capability failures) │ └── UnsupportedFeatureError └── ProviderError (provider returned an error response) ├── AuthError (401/403 — bad or missing API key) ├── BillingError (402 — payment/quota issue) ├── RateLimitError (429 — too many requests) ├── InvalidRequestError (4xx request-shape/resource errors) │ ├── ContextLengthError (input too long for model) │ └── UnsupportedModelError ├── TimeoutError (408/504 — request timed out) └── ServerError (5xx — provider-side failure)
LM15Error
¶
Bases: Exception
Base for all lm15 errors.
Errors keep the human-readable exception message in str(error) while
also exposing structured metadata for logging, telemetry, retries, and
programmatic handling.
TransportError
¶
Bases: LM15Error
High-level LM transport failure.
Provider LMs wrap lower-level lm15.transports.TransportError exceptions
into this class.
LockTimeoutError
¶
Bases: LM15Error
The credential-file lock (spec/auth.md AUTH-4) could not be taken
within the timeout: another lm15 process is refreshing the same
credential (lock_timeout, 2026-09-08).
Local and transient — a root-level class beside :class:TransportError,
never a :class:ProviderError (no provider was asked) and never an
:class:AuthError (nothing is wrong with the credential; AUTH-6).
Retryable. Carries path (the guarded file) and lock_path.
:class:lm15.auth.CredentialLockTimeout is this class and the builtin
TimeoutError at once, so except TimeoutError keeps working.
CollectionLimitError
¶
Bases: LM15Error
A local turn collector reached its budget, not a provider failure.
Non-retryable. partial_events preserves the accepted events without
copying their payloads. partial materializes them on demand as an
incomplete Turn. A byte-limit failure also exposes rejected_event:
it was received but not yielded or added to the collection. Process it
before resuming raw session reads if that content is needed.
StreamAssemblyError
¶
Bases: LM15Error
A stream could not be assembled into a Response without inventing a fact.
Three defects raise it (MAP-9 and MAP-3, contract change 2026-09-11-stream-completion):
- a tool call's fragments never carried a name (MAP-9): an unnamed call is not actionable (MAP-1), and guessing a name from the request dispatches the wrong function silently;
- the stream ended without an end event: the finish reason and usage never arrived, and reporting the text as a finished turn would invent both;
- an event arrived after the end event (MAP-3): it has no place in the Response, and dropping it would be silent loss.
All three are adapter or source defects, not model behaviour — every shipped dialect names a call on its first fragment and ends exactly once — so the message points at the adapter.
partial is everything that did assemble (text, thinking, other
parts, usage, finish reason) with the offending material left out, so a
caller that wants to salvage the turn can; part_index is the first
offending part (MAP-9 only).
What does NOT raise it: a failure after the end event that is not an
event — the source raising while it drains, or its close() raising.
The Response is complete; it is returned, and the failure is reported
as a :class:StreamCleanupWarning.
StreamCleanupWarning
¶
Bases: RuntimeWarning
A stream's source failed after the Response was already complete.
Emitted (warnings.warn) when, after the end event has been yielded,
the source raises while draining or its close()/aclose() raises.
The provider finished the turn and billed it; the Response is returned
unchanged. The failure is about the connection's afterlife, not the
answer, so it is never raised from response — a caller who wants it
programmatically reads ResponseStream.cleanup_errors, and a caller
who wants it fatal runs with -W error::lm15.errors.StreamCleanupWarning.
CapabilityError
¶
Bases: LM15Error
Requested capability is not supported by this provider adapter.
feature (MAP-13, 2026-09-14) is the config path of what was
refused — config.top_k, config.reasoning.thinking_budget,
messages[0].parts[1], tools[2] — so the caller's own policy
layer can drop it and retry without parsing the message. Absent
when the refusal is not about one addressable field.
AuthError
¶
RateLimitError
¶
BillingError
¶
TimeoutError
¶
Bases: ProviderError, TimeoutError
Provider request timed out.
Also subclasses the builtin TimeoutError so a user's bare
except TimeoutError: catches lm15 timeouts. ProviderError comes
first in the MRO, so lm15 metadata (code, status, ...) wins.
InvalidRequestError
¶
ContextLengthError
¶
UnsupportedModelError
¶
ServerError
¶
UnsupportedFeatureError
¶
NotConfiguredError
¶
UnknownModelError
¶
Bases: ConfigurationError
The router found no provider for a model string.
No routable provider prefix, no catalog match, and no rule matched
(spec/vocabularies.md unknown_model, 2026-09-08). Local and
pre-network: no provider was asked — a provider's own "no such model"
reply is :class:UnsupportedModelError. Carries model (the string
as requested); rules_tried and catalog_searched are the
reference's diagnostics, not contract payload.
AmbiguousModelError
¶
Bases: ConfigurationError
The catalog matched a model string under more than one provider, or
under more than one entry of one provider (ambiguous_model,
2026-09-08). The fix is an explicit provider: prefix. Carries
model and providers (every candidate, catalog order,
deduplicated).
with_credential_origin(error: ProviderError, origin: str) -> ProviderError
¶
Name where an AuthError's credential came from (AUTH-1 provenance,
amended 2026-09-19): its own line under the provider's message (a
paragraph of its own, so __str__ keeps the provider/HTTP suffix
on the provider's line), before the guidance, so a log line at 3 a.m.
answers "which identity?" without a second investigation. Non-auth
errors pass through.
with_credential_hint(error: ProviderError, hint: str) -> ProviderError
¶
Rewrite an AuthError's guidance for subscription (OAuth) adapters.
API-key adapters point at env vars; subscription adapters have no env var — the fix is re-running the provider CLI login. Non-auth errors pass through unchanged.
map_http_error(status: int, message: str, *, provider: str | None = None, env_keys: tuple[str, ...] = (), provider_code: str | None = None, request_id: str | None = None, retry_after: float | None = None) -> ProviderError
¶
Map HTTP status + message to a typed ProviderError.
Provider LMs extract the human-readable message and provider-specific code
from the provider's error body in their normalize_error override. This
function only maps HTTP status codes.
canonical_error_code(error: type[LM15Error] | LM15Error) -> str
¶
Return the canonical string code for an error class or instance.
error_class_for_code(code: str) -> type[LM15Error]
¶
Return the LM15Error subclass for a canonical string code.