Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.xpander.ai/llms.txt

Use this file to discover all available pages before exploring further.

Every SDK module raises ModuleException when an API call fails. The exception bundles the HTTP status code with a description so you can branch on the underlying cause.
from xpander_sdk import Agents
from xpander_sdk.exceptions.module_exception import ModuleException

agents = Agents()
try:
    agent = await agents.aget("non-existent-agent")
except ModuleException as e:
    print(f"[{e.status_code}] {e.description}")

ModuleException

from xpander_sdk.exceptions.module_exception import ModuleException
AttributeTypeDescription
status_codeintHTTP status code from the API response, or 500 for client-side failures.
descriptionstrHuman-readable error description (often the raw response body).
The exception’s string form is [{status_code}] {description}.

Common status codes

StatusCauseWhat to do
400Malformed request: invalid parameters, missing required fields, schema validation.Inspect description. Fix the call site.
401Missing or invalid API key.Verify XPANDER_API_KEY / Configuration.api_key. For self-hosted, confirm you’re using the Agent Controller key, not a cloud key.
403Authenticated but not authorized: wrong organization, agent owned by another user.Verify XPANDER_ORGANIZATION_ID. Check the agent’s access_scope.
404Resource doesn’t exist: bad agent/task/KB id, or the resource was deleted.Confirm IDs. Run agents.list() / tasks.list() to discover what exists.
409Conflict: duplicate creation, version mismatch on save, or worker environment collision.Reload the resource (task.areload()) and retry.
429Rate limit.Back off exponentially. The SDK does not auto-retry on 429.
500Server error, or any client-side exception (network, parsing, validation).Inspect description. For 500 specifically from the cloud, retry once or twice with backoff.

Branching on status

try:
    task = await tasks.aget(task_id="task_xyz")
except ModuleException as e:
    if e.status_code == 404:
        print("Task not found: was it deleted?")
    elif e.status_code in (401, 403):
        print("Auth issue: check credentials")
    else:
        raise

Retry strategy

The SDK does not retry failed requests automatically (except inside Events.start(), which retries SSE connections internally). For idempotent operations, wrap calls in your own retry loop:
import asyncio
from xpander_sdk.exceptions.module_exception import ModuleException

async def with_retry(coro_fn, *, attempts: int = 3, base_delay: float = 1.0):
    for attempt in range(1, attempts + 1):
        try:
            return await coro_fn()
        except ModuleException as e:
            if e.status_code in (429, 500, 502, 503, 504) and attempt < attempts:
                await asyncio.sleep(base_delay * (2 ** (attempt - 1)))
                continue
            raise

agent = await with_retry(lambda: agents.aget("agent-123"))
Avoid retrying:
  • 400 / 404: the request will keep failing.
  • acreate / acreate_task without an existing_task_id: you may create duplicate resources. Pass existing_task_id=... to make creation idempotent.

Validation errors from Tool.ainvoke

Tool invocations validate the payload against the tool’s auto-generated Pydantic schema before sending the request. A schema mismatch raises ValueError (not ModuleException):
try:
    result = await tool.ainvoke(agent_id="agent-123", payload={"bad": "shape"})
except ValueError as e:
    print(f"Payload didn't match schema: {e}")
Successful invocations still set is_error=True on the returned ToolInvocationResult if the remote endpoint returned an HTTP error. Check the result, not just exceptions:
result = await tool.ainvoke(agent_id="agent-123", payload={"city": "NYC"})
if result.is_error:
    print(f"Tool failed [{result.status_code}]: {result.result}")

Streaming errors

task.aevents() raises ValueError if the task wasn’t created with events_streaming=True. The underlying SSE connection logs warnings on parse failures and drops malformed events; it does not raise. Network failures propagate after the SDK’s internal retry budget is exhausted (see Events).