Skip to content

Handle review and errors

Review is a valid workflow outcome. A decision can return not_answerable with no_supported_answer, for example, and the authored flow can end in needs_review. That result has no execution.error and still contains useful business evidence. Send it to your review path or apply a separately authored policy; do not retry the model just because it abstained.

Technical failures have a different shape. Once a run is admitted, many failures return an ExecutionResult with execution.status == "failed" and a safe execution.error. Invalid admission input, capacity rejection, and other pre-run failures can raise ServiceError before a result exists. Caller cancellation propagates and does not guarantee a returned result.

from foliqant.core.errors import ServiceError


async def run_request(app, envelope):
    try:
        result = await app.run("demo", envelope)
    except ServiceError as error:
        return {"status": "rejected", "code": error.code.value, "retryable": error.retryable}
    if result.execution.status == "failed":
        error = result.execution.error
        assert error is not None
        return {"status": "failed", "code": error.code.value, "retryable": error.retryable}
    return result.model_dump(mode="json")

This is a minimal host decision, not a Foliqant API. A real service should retain the failed result's ledger under its own data policy and keep a durable request ID. Decide whether any external action can be repeated. retryable is supplied by the failing boundary and must not be inferred from the code. A timeout or cancellation does not prove a remote operation stopped; uncertain_effect requires reconciliation before retrying.

Canonical error codes

SafeError contains code, its fixed safe message, retryable, and an optional location. It contains no raw SDK exception. ServiceError carries the same code and retry flag before an execution result exists.

Code Meaning
invalid_configuration The workflow configuration is invalid.
invalid_input The input does not satisfy the required contract.
invalid_output An operation returned an invalid result.
unauthenticated Authentication is required.
forbidden The operation is not authorized.
not_found The requested resource was not found.
missing_binding A required input binding is unavailable.
timeout The operation exceeded its deadline.
budget_exhausted The execution budget is exhausted.
dependency_failure A required dependency is unavailable.
conflict The request conflicts with an existing operation.
uncertain_effect An external operation requires reconciliation.
cancelled The execution was cancelled.
capacity_exceeded The service has reached its admission limit.

These codes do not define HTTP status codes. An HTTP host should make a documented mapping suited to its API: malformed transport or invalid_input typically maps to a client error; capacity and dependency failures generally need a temporary failure response; needs_review is a valid response. Preserve the canonical error code in the body, and avoid returning validation or provider exception text. The HTTP example demonstrates one limited mapping; production hosts must set their own.

The library's model and MCP retry settings only repeat specifically eligible transient calls within their configured bounds. They do not retry a whole workflow or make an ambiguous external effect safe. See Configure limits and the exact result contract.