Errors, Classification and Rate Limits
Every exception type the library exports subclasses
AsyncBatchLLMError and keeps its built-in base
(TimeoutError, RuntimeError or ValueError), so existing except clauses
still match. BatchBudgetExceeded is documented with the budget settings on the
core page, LLMCallError with
single calls, and the artifact errors on the
artifacts page.
For which category each built-in classifier assigns, see Provider classifier categories.
Error classification
ErrorClassifier
async_batch_llm.strategies.ErrorClassifier
Bases: ABC
Abstract base class for classifying LLM provider errors.
classify
abstractmethod
Classify an exception and determine handling strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exception
|
Exception
|
The exception to classify |
required |
Returns:
| Type | Description |
|---|---|
ErrorInfo
|
ErrorInfo with classification details |
Source code in src/async_batch_llm/strategies/errors.py
ErrorInfo
async_batch_llm.ErrorInfo
dataclass
ErrorInfo(is_retryable: bool, is_rate_limit: bool, is_timeout: bool, error_category: str, suggested_wait: float | None = None, hint: str | None = None)
Structured information about an error.
Attributes:
| Name | Type | Description |
|---|---|---|
is_retryable |
bool
|
Whether the framework should retry the call. |
is_rate_limit |
bool
|
Whether this is a rate-limit error (triggers a coordinated cooldown rather than per-item backoff). |
is_timeout |
bool
|
Whether this is a timeout. |
error_category |
str
|
A short label for stats/logging. |
suggested_wait |
float | None
|
For rate limits, a server-suggested minimum wait in
seconds (e.g. parsed from a |
hint |
str | None
|
Optional human-readable remediation hint for the operator
(e.g. "top up your prepaid balance" for a 402). Surfaced in the
logs when the error is non-retryable so a misconfiguration doesn't
look like a generic API bug. |
DefaultErrorClassifier
async_batch_llm.DefaultErrorClassifier
Bases: ErrorClassifier
Default error classifier that handles common error types.
classify
Classify common errors with conservative defaults.
Precedence: framework errors, an HTTP 429 carried by the exception, validation and programming-error types, then message heuristics for untyped exceptions.
Source code in src/async_batch_llm/strategies/errors.py
GeminiErrorClassifier
async_batch_llm.GeminiErrorClassifier
Bases: ErrorClassifier
Google Gemini-specific error classification.
Dispatches on google.genai.errors.APIError.code (the HTTP status)
when the SDK is installed, then falls back to generic message/type-based
classification — so mocks and non-Gemini exceptions still classify
sensibly even without the [gemini] extra.
classify
Classify Gemini-specific errors.
Source code in src/async_batch_llm/classifiers/gemini.py
OpenAIErrorClassifier
async_batch_llm.OpenAIErrorClassifier
Bases: ErrorClassifier
Classifier for OpenAI SDK exceptions plus generic fallbacks.
Designed to be subclassed: provider-specific classifiers (e.g.
:class:OpenRouterErrorClassifier) override :meth:classify to handle
extra cases first and delegate to super().classify() for the rest.
OpenRouterErrorClassifier
async_batch_llm.OpenRouterErrorClassifier
PydanticAIErrorClassifier
async_batch_llm.PydanticAIErrorClassifier
Rate-limit strategies
RateLimitConfig controls cooldown and slow-start; a RateLimitStrategy
decides how long each cooldown lasts.
RateLimitStrategy
async_batch_llm.RateLimitStrategy
Bases: ABC
Strategy for handling rate limits.
on_rate_limit
abstractmethod
async
Called when rate limit detected.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
worker_id
|
int
|
ID of the worker that hit the rate limit |
required |
consecutive_limit_count
|
int
|
Number of consecutive rate limits |
required |
Returns:
| Type | Description |
|---|---|
float
|
Cooldown duration in seconds |
Source code in src/async_batch_llm/strategies/rate_limit.py
should_apply_slow_start
abstractmethod
Check if slow-start delay should be applied.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items_since_resume
|
int
|
Number of items processed since last rate limit |
required |
Returns:
| Type | Description |
|---|---|
tuple[bool, float]
|
(should_delay, delay_seconds) |
Source code in src/async_batch_llm/strategies/rate_limit.py
ExponentialBackoffStrategy
async_batch_llm.ExponentialBackoffStrategy
ExponentialBackoffStrategy(initial_cooldown: float = 300.0, max_cooldown: float = 600.0, backoff_multiplier: float = 1.5, slow_start_items: int = 50, slow_start_initial_delay: float = 2.0, slow_start_final_delay: float = 0.1)
Bases: RateLimitStrategy
Exponential backoff with progressive slow-start.
Initialize exponential backoff strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
initial_cooldown
|
float
|
Initial cooldown duration in seconds |
300.0
|
max_cooldown
|
float
|
Maximum cooldown duration in seconds |
600.0
|
backoff_multiplier
|
float
|
Multiplier for exponential backoff |
1.5
|
slow_start_items
|
int
|
Number of items for slow start after cooldown |
50
|
slow_start_initial_delay
|
float
|
Initial delay between items during slow start |
2.0
|
slow_start_final_delay
|
float
|
Final delay between items during slow start |
0.1
|
Source code in src/async_batch_llm/strategies/rate_limit.py
on_rate_limit
async
Calculate exponential backoff cooldown.
Source code in src/async_batch_llm/strategies/rate_limit.py
should_apply_slow_start
Apply progressive slow-start after rate limit.
Source code in src/async_batch_llm/strategies/rate_limit.py
FixedDelayStrategy
async_batch_llm.FixedDelayStrategy
Bases: RateLimitStrategy
Simple fixed delay between requests after rate limit.
Initialize fixed delay strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cooldown
|
float
|
Fixed cooldown duration in seconds |
300.0
|
delay_between_requests
|
float
|
Fixed delay between each request |
1.0
|
Source code in src/async_batch_llm/strategies/rate_limit.py
on_rate_limit
async
Return fixed cooldown duration.
Source code in src/async_batch_llm/strategies/rate_limit.py
should_apply_slow_start
Deadlines, aborts and interruptions
async_batch_llm.ItemDeadlineExceeded
Bases: TimeoutError, AsyncBatchLLMError
The end-to-end monotonic deadline for one logical item expired.
Source code in src/async_batch_llm/strategies/errors.py
async_batch_llm.BatchDeadlineExceeded
Bases: TimeoutError, AsyncBatchLLMError
The batch deadline stopped an accepted item before completion.
Source code in src/async_batch_llm/strategies/errors.py
async_batch_llm.BatchAbortedError
Bases: RuntimeError, AsyncBatchLLMError
An accepted collateral item was stopped by configured fail-fast.
Source code in src/async_batch_llm/strategies/errors.py
async_batch_llm.BatchInterruptedError
Bases: RuntimeError, AsyncBatchLLMError
A batch worker was cancelled before all accepted work drained.
Raised by process_all() when another task shuts down the processor or
cancels a worker. The caller itself was not cancelled; its own
cancellation still propagates as asyncio.CancelledError.
async_batch_llm.FrameworkTimeoutError
FrameworkTimeoutError(message: str, *, item_id: str | None = None, elapsed: float | None = None, timeout_limit: float | None = None)
Bases: TimeoutError, AsyncBatchLLMError
Timeout enforced by the batch-llm framework (asyncio.wait_for).
This distinguishes framework-level timeouts from API-level timeouts. Framework timeouts indicate the configured attempt_timeout was exceeded, whereas API timeouts indicate the LLM provider returned a timeout error.
Attributes:
| Name | Type | Description |
|---|---|---|
item_id |
ID of the work item that timed out (if available) |
|
elapsed |
Actual time elapsed in seconds |
|
timeout_limit |
Configured timeout limit in seconds |
Initialize FrameworkTimeoutError with structured context (v0.4.0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Human-readable error message |
required |
item_id
|
str | None
|
ID of the work item that timed out |
None
|
elapsed
|
float | None
|
Actual time elapsed before timeout |
None
|
timeout_limit
|
float | None
|
The timeout limit that was exceeded |
None
|
Source code in src/async_batch_llm/strategies/errors.py
async_batch_llm.RateLimitRetriesExceeded
RateLimitRetriesExceeded(message: str, *, item_id: str | None = None, rate_limit_retries: int | None = None)
Bases: AsyncBatchLLMError
A work item was retried after rate limits more than max_rate_limit_retries.
Rate-limit errors don't consume the max_attempts budget (they're a "wait
and try again", not a failed attempt), but they ARE bounded separately by
RetryConfig.max_rate_limit_retries so an endpoint that throttles forever
can't hang the batch indefinitely. When that bound is exceeded the framework
fails the item with this exception.
Attributes:
| Name | Type | Description |
|---|---|---|
item_id |
ID of the work item that exhausted its rate-limit retries. |
|
rate_limit_retries |
How many rate-limit retries were attempted. |
Source code in src/async_batch_llm/strategies/errors.py
Provider responses and structured output
async_batch_llm.EmptyResponseError
Bases: ValueError, AsyncBatchLLMError
The provider returned a billed response with no usable text.
Raised by the built-in models when the API call succeeded (and was
billed) but produced no content — e.g. a Gemini safety block, or an
OpenAI response whose finish_reason is length/content_filter
or a tool call.
Subclasses ValueError so existing handlers and classifiers keep
treating it as a deterministic, non-retryable failure. The tokens the
provider already billed are attached as _failed_token_usage so
failed-attempt accounting (WorkItemResult.token_usage) reflects the
real spend.
Added in v0.16.0.
Source code in src/async_batch_llm/strategies/errors.py
async_batch_llm.ProviderResponseError
ProviderResponseError(message: str, *, code: int | None = None, provider_error: Any = None, token_usage: TokenUsage | dict[str, int] | None = None)
Bases: AsyncBatchLLMError
Provider signaled failure inside an HTTP-200 response body.
Some gateways (notably OpenRouter) report upstream failures — no
provider available, upstream 5xx, upstream rate limits — as HTTP 200
with an error object in the body and no choices, so the SDK never
raises. These are typically transient routing failures: classifiers
treat them as retryable, and as rate limits when the embedded code
is 429.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
Numeric error code embedded in the body, if any. |
|
provider_error |
The raw error payload from the response body. |
Added in v0.16.0.
Source code in src/async_batch_llm/strategies/errors.py
async_batch_llm.StructuredOutputSchemaError
StructuredOutputSchemaError(message: str, *, token_usage: TokenUsage | dict[str, int] | None = None)
Bases: ValueError, AsyncBatchLLMError
A provider rejected a requested structured-output schema.
This is a deterministic request/schema compatibility failure, distinct from a model response that was accepted by the provider but failed local output validation. Classifiers do not retry it.
Source code in src/async_batch_llm/strategies/errors.py
async_batch_llm.StructuredOutputValidationError
Bases: ValueError, AsyncBatchLLMError
A provider returned output that failed local structured validation.
Provider-enforced output should normally make this impossible, but a malformed or schema-invalid successful response is retryable because a subsequent model attempt may be usable.
async_batch_llm.TokenTrackingError
Bases: AsyncBatchLLMError
Wrapper exception that preserves token usage from failed LLM calls.
When an LLM call fails (e.g., validation error), we still want to track the tokens that were consumed. This wrapper attaches token usage to exceptions that don't natively support it (e.g., built-in exceptions without dict).
Attributes:
| Name | Type | Description |
|---|---|---|
token_usage |
Dictionary with input_tokens, output_tokens, total_tokens, and optionally cached_input_tokens. |
Example
try: ... # LLM call that fails validation ... output = parse_response(response) ... except Exception as e: ... wrapped = TokenTrackingError(str(e), token_usage=tokens) ... wrapped.cause = e ... raise wrapped from e
Initialize TokenTrackingError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Human-readable error message |
required |
token_usage
|
TokenUsage | dict[str, int] | None
|
Token usage dict to preserve (input_tokens, output_tokens, etc.) |
None
|
Source code in src/async_batch_llm/strategies/errors.py
Configuration errors
async_batch_llm.MiddlewareContractError
Bases: ValueError, AsyncBatchLLMError
A preprocessing result violated accepted work-item identity or invariants.
async_batch_llm.QuotaScopeError
Bases: ValueError, AsyncBatchLLMError
A strategy quota scope could not be resolved safely before admission.