- Duale AI
- Documentation
- Python SDK
- SDK errors
SDK exceptions, error codes, and task outcomes
Reference for Duale AI SDK exceptions, HTTP rejection codes, task outcome error codes, and problem details used to classify and retry failures.
This page lists SDK exception types, problem details fields, HTTP rejection codes, task outcomes, document ingestion failures, and Library and Tool Host error codes.
- Typed exception hierarchy and exceptions outside it.
- Problem details fields and HTTP rejection codes.
- Task outcome error codes and stream error context.
- Document ingestion failure codes.
- Library request codes and Tool Host publication errors.
Summaries were generated by AI.
SDK operations raise both SDK exceptions and exceptions from Python, Pydantic, or a custom transport. This page lists each exception, the problem details Duale AI returns, every error code, and the automatic retries. To go from a symptom to a fix, use Troubleshoot the SDK. Task attachments use the Library errors.
Typed exception hierarchy
DualeAIError is the base class for the typed exceptions below. Import them from dualeai.
The default HTTP transport reports Task-stream network failures as DualeAIConnectionError after any applicable
retries. This reports a failure to observe the outcome; it does not prove that the Task failed. Preserve the Task ID
and read the stream error context.
LibraryUploadError carries the failed input’s attachment_key and optional part_number. completed_receipts
contains receipts that finished earlier in the same batch.
TaskStoppedError reports only reason and task_id. Stop is a separate final outcome with no problem details.
Exceptions outside the hierarchy
- A submission rejected by the open circuit breaker raises
RuntimeError. - Invalid client configuration raises
pydantic.ValidationErrorwhen you constructDualeAIConfigdirectly. WhenDualeAISDK()creates an invalid default config from the environment, it wraps that failure inRuntimeError. - Missing attachment paths raise
FileNotFoundError. A directory or empty file raisesValueErrorduring preparation. Attachment metadata that violates its model constraints raisespydantic.ValidationError. - Invalid Library update or polling arguments, or a non-empty task attachment upload without an Agent Identity
identifier, raise
ValueError. sdk.libraries.wait_for_document()raises the built-inTimeoutErrorwhen the whole polling operation exceeds its client-side timeout.- A cached
@activitywhose wait for scheduled work exceedsjob_timeoutraises the built-inTimeoutError. That timer excludes cache reads, cache writes, and any wait to enter a full queue; see Activity caching. - Custom transports can raise exceptions outside this hierarchy.
Problem details
Read exc.problem_details to classify a failure, decide whether to retry, or report it. This RFC 9457 object is
attached to DualeAIError for a failed Task, and to DualeAIAuthError, BusinessError, or DualeAIConnectionError
for an HTTP rejection of a Task stream or Library call.
Platform-token failures preserve valid problem details from the server; missing or malformed details leave
problem_details=None. Local validation, configuration, and file errors have no server details.
Branch on error_code, not on the detail text. Before your application repeats failed work, require
retryable=true. For an HTTP rejection, also honor retry_after_seconds when present. A failed Task never carries
retry_after_seconds, so apply your own backoff. Automatic retries describes what the SDK
already repeats for you.
HTTP rejection codes
An HTTP rejection arrives through DualeAIAuthError, BusinessError, or DualeAIConnectionError, according to the
exception hierarchy. For a Task submission, await the returned response to observe it.
An HTTP status alone does not promise a retryable value. Read the received field before deciding to retry.
METHOD_NOT_ALLOWED (405), NOT_ACCEPTABLE (406), GONE (410), REQUEST_BODY_TOO_LARGE (413), and
UNSUPPORTED_MEDIA_TYPE (415) name a request Duale AI cannot accept as sent; correct the request.
Task outcomes
A failed Task is raised by model() as DualeAIError. Its status describes the failed Task; it is not an HTTP
rejection of the stream. A stopped Task raises TaskStoppedError instead.
Codes about the models you configure start with LLM_.
Task-stream error context
When the default HTTP transport cannot recover a Task stream, read exc.context as well as exc.problem_details.
The context describes the last observation failure. Its error_code can name a local or end-to-end encryption error
that has no server problem details.
- Context field
operation- Meaning
task_run,task_continue, ortool_results.
- Context field
task_id- Meaning
- The Task whose stream the SDK was consuming.
- Context field
retry_attempt- Meaning
- Zero-based attempt index:
0is the initial attempt, and4is the last possible attempt. A non-retryable error can stop sooner.
- Context field
metadata["http_status"]- Meaning
- The received HTTP status, when known. It is independent of a status declared in
problem_details.status.
- Context field
metadata["logical_method"]- Meaning
- The method of the request the attempt sent. Credential renewal can fail before that request is sent.
- Context field
metadata["last_event_id"]- Meaning
- The last event the SDK received, when available.
These diagnostics do not establish a remote final outcome. Initial connection failures, other operations, and custom transports can provide less context. Include the available operation, Task ID, attempt index, and HTTP status when reporting a persistent stream failure.
Document ingestion failures
wait_for_document() returns a failed document instead of raising when processing fails. Its document.failure can
be None, so a failure code is not always available. A failed document will not become ready on its own.
When details are present, they use RFC 9457 with retryable=false; optional fields can be absent or null. Treat
missing details or an unlisted error_code as an unknown cause. Upload again only if a new upload can change the cause
below.
A failed document still occupies its Library until you delete it.
Library request codes
A Library call can also fail before any document is processed. These codes arrive in problem_details.error_code.
Status
ACR_STEPUP_REQUIRED | DOCUMENT_UPLOAD_TOO_LARGE | DOCUMENT_TAGS_TOO_LARGE | TAGS_TOO_LARGE | UPLOAD_SESSION_EXPIRED | DOCUMENT_PURGED | CONFLICT |
|---|---|---|---|---|---|---|
| 401 | 413 | 413 | 422 | 410 | 410 | 409 |
Meaning
ACR_STEPUP_REQUIRED | DOCUMENT_UPLOAD_TOO_LARGE | DOCUMENT_TAGS_TOO_LARGE | TAGS_TOO_LARGE | UPLOAD_SESSION_EXPIRED | DOCUMENT_PURGED | CONFLICT |
|---|---|---|---|---|---|---|
| The operation needs a higher authentication level, such as deleting a whole Library. | A document exceeds the 500 MiB upload limit. | Document tags exceed 4 KiB of serialized JSON. | Library tags exceed 4 KiB of serialized JSON. | The upload session expired before completion. | The document can no longer be restored. | With detail Library changed; retry against the current path: the Library changed while the call ran, and the operation did not run. |
Next action
ACR_STEPUP_REQUIRED | DOCUMENT_UPLOAD_TOO_LARGE | DOCUMENT_TAGS_TOO_LARGE | TAGS_TOO_LARGE | UPLOAD_SESSION_EXPIRED | DOCUMENT_PURGED | CONFLICT |
|---|---|---|---|---|---|---|
| Use the Dashboard; an API token cannot reach that level. | Reduce or split the document before preparing and uploading it again. | Shorten the tags; see Library limits. | Shorten the tags; see Library limits. | Start a new upload. | Upload the file again. | Read the Library again, because its current path decides access, then repeat the call against that path if you are still permitted to. |
Tool Host errors
sdk.serve() publishes the whole manifest. The four publication rejections below publish nothing and raise
BusinessError without retrying. Read problem_details.error_code; Tool
limits lists the limits.
- Code
TOO_MANY_TOOLS- Status
- 422
- Meaning
- The manifest declares more Tools than one Tool Host can publish.
- Next action
- Serve fewer Tools from this process, or split them across Tool Hosts under separate Agent Identities.
- Code
REQUEST_BODY_TOO_LARGE- Status
- 413
- Meaning
- The registration request is larger than 4 MiB. The same code rejects an oversized Tool Result.
- Next action
- Shorten schemas and descriptions, or split the Tools across Tool Hosts under separate Agent Identities.
- Code
TOOL_SCHEMA_TOO_LARGE- Status
- 422
- Meaning
- One
@toolinput schema is larger than the per-Tool ceiling. - Next action
- Narrow that Tool’s parameter model and shorten its field descriptions.
- Code
RESERVED_TOOL_NAME- Status
- 422
- Meaning
- A
@toolfunction uses a reserved name:exec_agent,finish,list_agents,search_available_tools,context_ls,context_search,context_grep,context_cat,conversation_ls,conversation_search,conversation_grep, orconversation_cat. The Python SDK refuses such a name locally:@toolraisesValueErrorwhen it registers the function, beforesdk.serve()sends any manifest. - Next action
- Rename the function. Duale AI does not release a reserved name.
- Code
RATE_LIMIT_EXCEEDED- Status
- 429
- Meaning
- One Agent Identity sent registration, heartbeat, or deregistration requests faster than Duale AI accepts. This code carries no
retry_after_seconds. - Next action
- Run one Tool Host process per Agent Identity. The SDK stops
serve()after three consecutive failed heartbeats, or two consecutive401or403refusals.
Automatic retries
Automatic retries depend on the operation:
- Task stream. Each stream operation has at most five attempts, including the initial attempt. The default HTTP
transport retries a received HTTP
500–599, a problem response withretryable=true, or a recoverable interruption such as a lost connection. A received5xxis retryable even when its problem details omit the flag or set it tofalse. These causes share one budget, including credential renewal failures within an attempt; receiving events does not reset it. Until Duale AI accepts the stream, the SDK resubmits with the same Task identifier. After that, it reconnects and resumes after the last event it received. A stream that closes without a final event also triggers recovery. An end-to-end encryption failure ends the stream without retrying. A receivedtask.completed,task.error, ortask.stoppedends consumption; the SDK does not retry a finaltask.error, even if its problem details sayretryable=true. Recovery uses exponential backoff with random variation, notretry_after_seconds. The attempt limit sets no total duration. - Inside the Task. Duale AI retries failing work on its side. The Task still ends with exactly one outcome.
- SDK Tool callable. Retries are opt-in. Tool retries defines the shared deadline and the risk of repeating an external Effect.
- Library API. The SDK renews its credential and its encryption key once automatically when either is refused or
expires. It does not retry a request rejected by a business rule, or a failed request whose outcome is unknown.
wait_for_document()keeps polling while reads succeed and the document is not yetreadyorfailed. If an error remains after that recovery, polling stops. - File upload parts. Each part of a file upload retries network and timeout failures plus HTTP
500,502,503, and504, for at most five attempts. - Cached activity. Set
@activity(max_retries=...); Activity caching defines the defaults, the timeout, and the retry-safety requirements.
Reconnecting, or resubmitting the same Task identifier, replays only the events Duale AI still keeps. It neither reruns completed work nor recovers an expired final event. There is no separate Task Result lookup, so save the Task ID and its final outcome in your application.
Reuse a task_id only for an exact retry of the same request; task_id states why.
Every Task carries an absolute deadline; ask states the default. A Task that does not
finish in time fails with TASK_DEADLINE_EXCEEDED. Deadlines and Stop
explains what can still happen after the deadline.
Circuit breaker
The process-local circuit breaker counts retryable failed Task outcomes and DualeAIConnectionError, unless its
problem details say retryable=False. Stops, cancellation, other local exceptions, and explicitly non-retryable Task
failures do not count. A completed Task resets the failure count.
By default, ten consecutive counted failures open the circuit and block new Task submissions with RuntimeError.
After 30 seconds, the SDK allows one Task submission to check for recovery. If it succeeds, the circuit closes. Set
BackpressureConfig.failure_threshold and recovery_timeout_seconds to change those defaults; see
BackpressureConfig.