Errors and recovery
AML rejects invalid placement, invalid data, policy violations, provider failures, cancellation, limits, and resource conflicts. The runtime does not widen permissions or fall back to host execution to make a workflow appear successful.
Public AML error classes
Section titled “Public AML error classes”| Class | Stable identity | Typical cause | Retry guidance |
|---|---|---|---|
EvaluationError | name === "EvaluationError" | Invalid AML value/placement, missing scope, cycle, policy, limit, or structured-output contract | Fix the workflow or configuration; do not blindly retry |
ToolInputError | name === "ToolInputError" | A callable Tool receives input that fails JSON or input-schema validation | Correct application input or ask the Agent to correct a model-selected call |
ToolOutputError | name === "ToolOutputError" | A callable Tool returns data that fails output validation or the stable JSON boundary | Fix the Tool output/schema; retry only when the underlying operation is safe |
WorkspaceConflictError | code === "AML_WORKSPACE_CONFLICT" | Another writer owns the same <Workspace /> identity | Serialize, choose another identity, or retry acquisition with backoff |
Only WorkspaceConflictError defines an AML-specific code. Do not invent a code for EvaluationError, ToolInputError, or ToolOutputError; preserve the class/name and cause instead.
import { WorkspaceConflictError } from "@aml-jsx/sdk"
try { await runtime.evaluate(<Workflow />)} catch (error) { if (WorkspaceConflictError.is(error)) { return queueForLater(error.workspaceId) }
if (error instanceof Error) { logger.error({ name: error.name, message: error.message, cause: error.cause }, "AML failed") } throw error}The static WorkspaceConflictError.is() check is useful across duplicated physical SDK copies and does not require relying on instanceof.
Common EvaluationError categories
Section titled “Common EvaluationError categories”The class is intentionally broad because the error message identifies the invalid contract. Common messages include:
evaluate() is only available while an AML component is active— move the call into an async AML component.Tools can only be called while an AML component is active— call the function returned bydefineTool()during active component evaluation, or use its explicit low-levelexecute(input, context)API only in provider/test infrastructure.<Tool> is only valid inside <Agent>,<Mcp> is only valid inside <Agent>, or<Skill> is only valid inside <Agent>— move the capability into its owning<Agent />plan.<File> requires an enclosing <Workspace> or <Sandbox>— place the write under the intended active filesystem owner and use a read-write Sandbox for guest writes.<Include path> requires an enclosing <Workspace> or <Sandbox>— use an active workflow filesystem path or switch to application-ownedsrc.an oversized <Include> requires a containing <Agent>— move the Include into the Agent that will receive and read its staged file reference, or raise/removemaxBytes.A nested <Sandbox> cannot widen read-only access to read-write— narrow the child<Sandbox />policy or change the parent policy.AML evaluation exceeded maxAgentCalls ...ormaxDepth ...— increase a deliberate budget or reduce workflow breadth/depth.Structured evaluate() must resolve to exactly one <Agent>— make the schema-bearing evaluation target one<Agent />result.... schema validation failed— inspect the schema and the provider’s structured response.
Path validation also rejects absolute paths, parent traversal, and paths that escape the active resource root. Treat these failures as authoring or policy defects, not transient provider errors.
Tool validation boundaries
Section titled “Tool validation boundaries”defineTool() validates the Tool definition when it is created. Both application calls and Agent-driven calls use the same registered input/output validation boundary. A Tool input failure means the authored executor did not run for that invalid input. An output failure means the Tool may already have performed its external effect; correcting the schema does not undo it.
const ReadOrder = defineTool({ name: "read_order", description: "Read one order", input: z.object({ id: z.string().min(1) }), output: z.object({ total: z.number() }), execute: async ({ id }) => await orders.read(id),})Keep Tool implementations idempotent where possible, validate authorization inside the Tool, and avoid returning secrets or arbitrary non-JSON values.
Provider and transport failures
Section titled “Provider and transport failures”Provider errors are wrapped at AML boundaries while preserving the original cause where the adapter supplies one. The underlying error may be a process launch error, ACP/session error, Sandbox acquisition/release error, network error, or Workspace storage error. Log the provider name, run ID, span ID, class/name, message, and cause without logging prompt content by default.
Agent traces separate the failed boundary from cleanup: agent.turn reports the active turn failure, agent.cleanup reports session teardown independently, and agent.session closes only after cleanup. On the shared ACP process path, sandbox.process state="wait_failed" means AML could not observe completion; it does not prove the process exited. Likewise, kill_completed proves only that the provider’s termination request resolved. Use the provider control plane when an actual remote terminal state matters.
Retry only when all of these are true:
- the failure is classified as transient;
- the operation has not committed an irreversible external effect, or is idempotent;
- the retry remains inside the caller’s deadline and AML budgets;
- Workspace lock and revision semantics are respected.
Do not retry invalid placement, schema, path, allowlist, read-only policy, or exceeded-limit failures without changing the input/configuration.
Cancellation and cleanup
Section titled “Cancellation and cleanup”Cancellation rejects the evaluation after propagating the signal to active work. It does not roll back files, Tool calls, provider mutations, or network requests that already completed. finish remains the request-level place to record status: "error"; trace span.end events expose the closing span status and duration. agent.session state="cancellation_requested" records when cancellation reached the provider session, while acp.session.cancel records the ACP notification attempt.
A recovery envelope
Section titled “A recovery envelope”async function runWithRecovery(workflow: AmlRenderable): Promise<string> { try { return await runtime.evaluate(workflow) } catch (error) { if (WorkspaceConflictError.is(error)) { await sleepWithJitter() return await runtime.evaluate(workflow) }
if (isTransientProviderError(error)) { return await retryOnce(workflow) }
throw error }}The predicates in this example are application policy, not AML exports. Keep them narrow and observable; a generic catch/retry loop can duplicate Agent calls or overwrite newer Workspace revisions.