Exceptions Reference
Alquimia uses typed exceptions to signal specific failure conditions in the agent execution loop. All core exceptions carry a control_id field for log correlation — this is the same control_id that appears on the command or response that triggered the failure.
alquimia-core exceptions
Section titled “alquimia-core exceptions”Source: src/alquimia/core/exceptions.py
ControllerException
Section titled “ControllerException”Base class for all controller-level exceptions. All subclasses inherit control_id.
@dataclassclass ControllerException(Exception): control_id: strstr(exception) returns [control_id=<id>] <ClassName> for all subclasses.
Unknown event exceptions
Section titled “Unknown event exceptions”These are raised by _handle_event() when a command or response arrives that has no registered handler. They indicate a bug in the event dispatch table or an unregistered event type.
| Exception | Raised when |
|---|---|
UnknownAgentDiscoveryReceived | An AgentDiscovery response arrives with no matching handler |
UnknownHumanApprovalReceived | A HumanApprovalRequired response arrives with no matching handler |
UnknownToolExecutionReceived | A tool execution response arrives with no matching handler |
UnknownResponseExecutionReceived | A ResponseInference response arrives with no matching handler |
UnknownShieldExecutionReceived | A ShieldInference response arrives with no matching handler |
UnknownToolSchemaReceived | A ToolSchema response arrives with no matching handler |
UnknownAttachmentNormalizationReceived | An attachment normalization response arrives with no matching handler |
UnknownContextFlushExecutionReceived | A ContextFlush response arrives with no matching handler |
Fields: control_id: str
Handling: These exceptions indicate a programming error, not a runtime condition. They should not be caught in application code — let them propagate and surface as 500 errors.
Iteration guard exceptions
Section titled “Iteration guard exceptions”These are raised when a tool exceeds its configured execution limits. They protect against infinite loops and runaway tool calls.
MaxIterationsReachedException
Section titled “MaxIterationsReachedException”Raised when a tool has been called more times than max_steps allows in the current evaluation strategy.
@dataclassclass MaxIterationsReachedException(ControllerException): iterations: int # current iteration count control_id: str toolname: str # the tool that exceeded the limit max_iterations: int # the configured limitExample message:
[control_id=abc-123] Tool 'search_tickets' exceeded the maximum number of iterations —allowed: 10, current: 11.MaxIterationVolumeReachedException
Section titled “MaxIterationVolumeReachedException”Raised when a tool has been called more times concurrently than max_concurrent_tools allows.
@dataclassclass MaxIterationVolumeReachedException(ControllerException): iterations: int max_iterations: int control_id: str toolname: strMaxIterationsHardReachedException
Section titled “MaxIterationsHardReachedException”Subclass of MaxIterationsReachedException. Raised when the hard iteration limit is reached — execution is permanently blocked for this tool in the current run.
MaxIterationVolumeHardReachedException
Section titled “MaxIterationVolumeHardReachedException”Subclass of MaxIterationVolumeReachedException. Raised when the hard concurrent call limit is reached.
Handling: Catch MaxIterationsReachedException (catches both soft and hard variants) to surface a user-facing error:
from alquimia.core.exceptions import MaxIterationsReachedException
try: response = await evaluate(controller, command, registry)except MaxIterationsReachedException as exc: logger.error(f"Tool {exc.toolname} exceeded iteration limit: {exc}") # Return a graceful error to the useralquimia-runtime exceptions
Section titled “alquimia-runtime exceptions”Source: runtime/src/utils/exceptions.py
These exceptions are raised by the runtime’s database and storage layers.
Database exceptions
Section titled “Database exceptions”| Exception | Raised when |
|---|---|
EntityDoesNotExist | A requested database record (agent, secret, parameter, topic, file) does not exist |
EntityAlreadyExists | An attempt is made to create a record that already exists (duplicate key) |
Fields: none (plain Exception subclasses)
HTTP mapping: The runtime’s routers catch these and return appropriate HTTP status codes:
| Exception | HTTP status |
|---|---|
EntityDoesNotExist | 404 Not Found |
EntityAlreadyExists | 409 Conflict |
Storage exceptions
Section titled “Storage exceptions”| Exception | Raised when |
|---|---|
FileDoesNotExist | A requested file is not found in S3/blob storage |
FileAlreadyExists | An attempt is made to upload a file that already exists in storage |
Fields: none
HTTP mapping:
| Exception | HTTP status |
|---|---|
FileDoesNotExist | 404 Not Found |
FileAlreadyExists | 409 Conflict |
Agent execution exceptions
Section titled “Agent execution exceptions”A2AMaxDepthReached
Section titled “A2AMaxDepthReached”Raised when agent-to-agent (A2A) delegation exceeds the configured maximum depth.
class A2AMaxDepthReached(Exception): passThe maximum depth is configured via A2A_MAX_DEPTH (default 5). This prevents infinite delegation chains where Agent A calls Agent B calls Agent A.
HTTP mapping: 500 Internal Server Error with message "An internal error occurred".
Handling: If you expect deep A2A chains, increase A2A_MAX_DEPTH. If you see this in production, it likely indicates a circular delegation configuration.
AgentHoldedException
Section titled “AgentHoldedException”Raised when an agent is temporarily unavailable — its agentspace has been put on hold.
class AgentHoldedException(Exception): passHTTP mapping: 500 Internal Server Error.
Handling: Check the agentspace status in the registry. An agent on hold will not accept new inference requests until the hold is released.
Exception correlation
Section titled “Exception correlation”All ControllerException subclasses carry control_id. This is the same ID that appears on the command that triggered the failure. Use it to find the relevant worklog record:
from alquimia.core.exceptions import MaxIterationsReachedExceptionfrom alquimia.core.worklog import Worklog
try: response = await evaluate(controller, command, registry)except MaxIterationsReachedException as exc: # Find the tool execution record that triggered the limit record = worklog.find_by_id("ServerToolExecution", exc.control_id) logger.error(f"Tool exceeded limit. Last call data: {record.data}")Related pages
Section titled “Related pages”- evaluate() — where most exceptions surface
- Event Model — the commands and responses that carry
control_id - Observability — correlating exceptions with traces and logs
Source
Section titled “Source”Alquimia-ai/alquimia-core—src/alquimia/core/exceptions.pyAlquimia-ai/alquimia-runtime—runtime/src/utils/exceptions.py