Skip to Content
DocsErrors

Errors

Every error the framework raises subclasses LoomError. The hierarchy lets you catch broadly (except LoomError) or narrowly (except RateLimitError) without surprises.

LoomError ├── ConfigError (Agent / Memory / Model misconfiguration) ├── ModelError │ ├── TransientModelError (retried automatically) │ │ ├── RateLimitError (HTTP 429, Retry-After honored) │ │ └── ... (5xx, network blips, timeouts) │ ├── PermanentModelError (NOT retried) │ │ ├── AuthenticationError (HTTP 401) │ │ ├── InvalidRequestError (HTTP 400 — bad prompt or args) │ │ └── ContentFilterError (provider safety filter) │ └── OutputValidationError (output_schema validation failed) ├── ToolError (a tool raised — wrapped into ToolResult.error) ├── PermissionDenied (permissions returned deny_) ├── BudgetExceeded (per-run or per-user cap hit) ├── MemoryStoreError (Memory backend failure) ├── RuntimeJournalError (Runtime journal failure) ├── SandboxError (FilesystemSandbox / SubprocessSandbox blocked the call) ├── MCPError (MCP server failure) ├── FreshnessError (data lineage policy violation) ├── LineageError (data lineage policy violation) └── CancelledByUser (user cancelled the run)

What raises what

ConfigError

Misconfigured Agent. Examples:

  • Agent("...") without model= (since 0.2).
  • Agent("...", model="bad-spec") for an unknown string.
  • Agent("...", architecture="bogus") for an unknown architecture.

Catch at startup; not retryable.

ModelError and subclasses

Raised by the model adapter. The framework’s RetryPolicy retries TransientModelError automatically (3 attempts default). PermanentModelError is fail-fast.

  • RateLimitError. Provider returned 429. The retry honors any Retry-After header.
  • AuthenticationError. Your API key is wrong / missing / revoked.
  • InvalidRequestError. Request body malformed (oversize prompt, bad tool definition, etc.).
  • ContentFilterError. Provider’s safety filter rejected the request.
  • OutputValidationError, output_schema= Pydantic validation failed. Handled separately: the framework appends the error to the conversation and asks the model to retry.

See RetryPolicy + error taxonomy.

ToolError

A tool raised an exception. The framework wraps it into ToolResult(ok=False, error=str(exc)) so the model sees the failure in the next turn and can decide how to react. Your code typically doesn’t catch ToolError directly. The agent loop handles it.

PermissionDenied

The permissions layer returned deny_(reason). The agent loop reports this as a ToolResult with an error message; doesn’t usually surface as a Python exception unless you opt into raising via custom hooks.

BudgetExceeded

A per-run or per-user budget cap was exceeded. The run terminates cleanly with result.interrupted = True, result.interruption_reason = "budget:...". You can also intercept by catching BudgetExceeded if you need to react.

See Per-user budget caps.

MemoryStoreError

The Memory backend’s underlying store failed (Postgres connection dropped, Redis cluster unreachable, etc.). Often transient. Your runtime layer’s reconnect logic handles re-establishing the connection.

RuntimeJournalError

The runtime journal failed to write. Rare; usually points at disk-full or a misconfigured DSN.

SandboxError

A sandbox blocked the call. Subclasses:

  • PathEscapeError, FilesystemSandbox rejected a path argument.
  • Subprocess timeout / pickle failure, SubprocessSandbox.

Returned to the model as a ToolResult error; the model adapts.

MCPError

MCP server crashed or returned a malformed response. Subclasses include details. Exit code for stdio servers, HTTP status for HTTP servers.

CancelledByUser

The user broke out of the agent.stream() iterator (or otherwise cancelled). Run terminates cleanly with interrupted=True.

Catching broadly

from loomflow import Agent, LoomError try: result = await agent.run("...") except LoomError as exc: log.error("agent run failed: %s", exc) return user_friendly_error()

Catching narrowly

from loomflow import Agent, BudgetExceeded from loomflow.core.errors import AuthenticationError, RateLimitError try: result = await agent.run("...") except AuthenticationError: raise HTTPException(401, "API key invalid") except RateLimitError: raise HTTPException(429, "rate limit; retry later") except BudgetExceeded as exc: raise HTTPException(402, f"budget exceeded: {exc}")

classify_model_error(exc)

Useful when you’re catching a raw provider exception and want the framework’s typed wrapper:

from loomflow.governance import classify_model_error try: response = await my_provider_call() except Exception as exc: typed = classify_model_error(exc) # typed is now one of the Loom error subclasses raise typed from exc

Errors vs. interruptions. A RunResult with interrupted=True is a clean termination. Budget cap hit, user cancelled, max_turns reached. An exception is a failure. Something the framework couldn’t recover from. Most production code wants to handle both: check result.interrupted after a successful return, catch LoomError for the rest.

Last updated on