Skip to content

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.

ConfigurationError

Bases: LM15Error

Local SDK or provider-adapter configuration failure.

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.

ProviderError

Bases: LM15Error

The provider returned an error response.

AuthError

Bases: ProviderError

Authentication failed — invalid, expired, or missing API key.

RateLimitError

Bases: ProviderError

Rate limited by the provider (HTTP 429).

BillingError

Bases: ProviderError

402 — billing or payment issue.

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

Bases: ProviderError

Bad request shape or invalid provider resource (4xx).

ContextLengthError

Bases: InvalidRequestError

The input exceeds the model's context window.

UnsupportedModelError

Bases: InvalidRequestError

Model not found, unavailable, or unsupported by the provider.

ServerError

Bases: ProviderError

Provider-side failure (5xx).

UnsupportedFeatureError

Bases: CapabilityError

Feature not supported by this provider adapter.

NotConfiguredError

Bases: ConfigurationError

No API key or required provider configuration was found.

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.