Skip to content

Retry policy

Retries are applied at the trusted executor boundary.

Configure retries

from schemarouter import RetryPolicy, RunConfig

config = RunConfig(
    retry=RetryPolicy(
        max_attempts=3,
        initial_backoff_seconds=0.25,
        backoff_multiplier=2.0,
        max_backoff_seconds=5.0,
    )
)

result = await router.ainvoke(request, config=config)

Read-only by default

Automatic retry is enabled only when the endpoint is explicitly classified as read_only=True.

This prevents a transient failure from accidentally repeating a write operation.

read_only=True
 -> max_attempts may apply

read_only=False or unknown
 -> one attempt by default

Trusted local code can set retry_non_read_only=True, but that is an explicit idempotency decision.

What is not retryable

Schema contract violations fail immediately. Trusted invokers can also raise NonRetryableInvocationError when repeating the same call cannot safely recover.

Examples:

  • invalid tool output;
  • an output enum/type violation;
  • invalid current input schema;
  • stale schema fingerprint;
  • stale invoker binding;
  • policy rejection.

Retrying these would hide a deterministic correctness problem rather than recover a transient transport failure.

The built-in OpenAPI and OPTIMADE HTTP invokers classify 408, 425, 429, 500, 502, 503, and 504 as retryable HTTP statuses. Other HTTP error statuses fail fast. Deterministic transport-contract failures such as an oversized response, malformed declared JSON, or an invalid OPTIMADE success shape also fail fast.

Custom invokers are fail-closed by default: an ordinary Exception is not automatically retried. A trusted adapter must classify a genuinely transient failure explicitly with TransientInvocationError. Use InvocationUnavailableError when the current access path is temporarily unavailable and may also participate in precompiled read-only fallback/health handling.

NonRetryableInvocationError remains available for explicit permanent failures, while IndeterminateInvocationError means an operation may still complete after timeout/cancellation and must never be automatically repeated. Unknown adapter/programming/configuration errors execute once and propagate without retry.

Choosing a policy

Keep the default max_attempts=1 unless the underlying operation and transport semantics justify automatic retry.

max_backoff_seconds caps every retry delay, including the first delay requested by initial_backoff_seconds.

Retry backoff also consumes the run's wall-clock budget. If the requested delay would extend past ExecutionBudget.max_elapsed_seconds, SchemaRouter stops at the remaining budget boundary instead of sleeping for the full backoff interval.

For remote HTTP APIs, consider whether the endpoint itself is idempotent in addition to the HTTP method or schema classification.