Error handling.
Run and stream errors, retry behaviour, and partial results after a failure.
MatildaAgentRunError
Raised on HTTP-level failures (non-2xx response from the server).
class MatildaAgentRunError(MatildaError):
status: int # HTTP status code
response_text: str # Raw response bodyMatildaAgentStreamError
Raised by run() when the stream emits an error event (unless throw_on_stream_error=False). Carries the full AgentRunResult with whatever was collected before the error.
class MatildaAgentStreamError(MatildaError):
code: str # ChatErrorCode of the first error ('unknown' when empty)
errors: list[StreamErrorDetail]
result: AgentRunResultfrom matilda_agent_sdk import MatildaAgentRunError, MatildaAgentStreamError
try:
await runner.run(agent, prompt)
except MatildaAgentStreamError as err:
print("Stream error code:", err.code)
print("Partial output before error:", err.result.final_output)
print("All errors:", err.errors)
except MatildaAgentRunError as err:
print("HTTP error status:", err.status)
print("Response body:", err.response_text)SafetyReplaceError
Raised by stream_text(), run_text(), stream_object(), and run_object() when the server replaces the output via a safety filter. str(err) contains the replacement text (or empty string), and categories lists the safety categories.
class SafetyReplaceError(MatildaError):
categories: list[str]MatildaObjectParseError
Raised by stream_object() / run_object() when the response cannot be parsed as JSON or fails pydantic validation — e.g. a truncated stream (see Structured output). raw holds the full response text; cause is the underlying json.loads or pydantic error. Safety replacement does not surface here — it raises SafetyReplaceError first.
class MatildaObjectParseError(MatildaError):
raw: str
cause: BaseException | strClient SDK errors (re-exported)
The client SDK's error types are re-exported unchanged: MatildaError (shared base), MatildaAPIError (status, response_text), QuotaExceededError (structured 429 quota attributes), MatildaStreamError (code), and UploadError. AuthError (code, retryable) is importable from matilda_client.
Chat error codes (ChatErrorCode)
These codes are emitted via the ResponseError stream event and appear in AgentRunResult.errors (mirrored on StreamErrorDetail.code):
| Code | Description |
|---|---|
internal_error | Server-side failure. |
upstream_unavailable | The AI model is not responding. |
rate_limited | Too many requests. |
content_blocked | Safety filter blocked the content. |
stream_aborted | The stream was interrupted before completion. |
deadline_exceeded | The response did not finish before the deadline. |
context_too_large | The conversation is too long for the model. |
url_read_incomplete | A URL the server tried to read did not finish loading. |
request_budget_exceeded | The request exceeded its server-side budget. |
stalled | No SSE events for the configured stall window. |
unknown | Unclassified error (old server without typed codes). |
Stall watchdog
The streaming parser arms an idle-event watchdog. If no SSE event arrives for stall_timeout_ms milliseconds, the stream is considered dead and aborted with a 'stalled' error.
- Default:
45_000ms (DEFAULT_AGENT_STALL_TIMEOUT_MS) - Disable: Pass
stall_timeout_ms=0(not recommended)
Retry behaviour
The SDK retries retryable errors within a single turn. Retries are controlled by max_retries (default: 0 — no retries; DEFAULT_MAX_RETRIES).
Retryable errors:
- HTTP 429 (rate limited), 408 (request timeout), 5xx (server errors)
- Network errors:
httpx.ConnectError,httpx.ConnectTimeout,httpx.ReadTimeout,httpx.WriteTimeout,httpx.PoolTimeout,httpx.RemoteProtocolError - SSE stall watchdog (
stallederror code) TimeoutError
Retry delay: Exponential backoff with jitter (1 s base), capped at 30 seconds.
Important: Retries only happen when no text has been yielded to the consumer yet. Retrying past that point would replay text the caller already has.
When a retry occurs, a TurnRetrying event is emitted:
async for event in runner.stream(agent, prompt, max_retries=3):
if event.type == "turn.retrying":
print(f"Retry {event.attempt}/{event.max_retries} in {event.delay_ms}ms: {event.error['message']}")Error handling example
import asyncio
from matilda_agent_sdk import (
MatildaAgentRunError,
MatildaAgentStreamError,
Runner,
SafetyReplaceError,
)
async def main():
runner = Runner()
try:
text = await runner.run_text(agent, "Hello!")
print(text)
except MatildaAgentStreamError as err:
print(f"Stream error: {err.code} — {err}")
print("Partial output:", err.result.final_output)
except MatildaAgentRunError as err:
if err.status == 401:
print("Session expired — re-authenticate.")
elif err.status == 429:
print("Rate limited — slow down.")
else:
print(f"API error {err.status}: {err.response_text}")
except SafetyReplaceError as err:
print(f"Safety filter: {', '.join(err.categories)}")
asyncio.run(main())