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.
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.