Skip to content
RelPrim docsTimeouts

Reliability

Timeouts

Bound each attempt with TimeoutPolicy. What is timed, how RelPrim's own deadline differs from a TimeoutError raised by your client, and how timeouts interact with retries and fallbacks.

A timeout turns a call that hangs into a failure RelPrim can act on: retry it, fall back, or report it. RelPrim timeouts are asyncio deadlines applied to each primary attempt.

Usage

from relprim import TimeoutPolicy, resilient


@resilient(retries=2, timeout=10)
async def call_provider(prompt: str) -> str:
    return await provider.generate(prompt)


@resilient(retries=2, timeout=TimeoutPolicy(seconds=2.5))
async def call_fast_provider(prompt: str) -> str:
    return await fast_provider.generate(prompt)

timeout accepts a number of seconds or a TimeoutPolicy. In the builder API, use .with_timeout(TimeoutPolicy(seconds=10)).

seconds must be a finite int or float greater than zero. Like every duration in RelPrim, it is expressed in seconds.

What is timed

The deadline covers one call of your operation, within one primary attempt:

  • each primary attempt gets a fresh deadline, so retries=2, timeout=10 can spend up to thirty seconds in attempts, plus the delays between them;
  • retry and rate-limit delays between attempts are not part of any deadline;
  • validation of the returned value runs after the call and is not timed;
  • fallback candidates run without the timeout policy.

To bound the entire execution, wrap the call yourself:

import asyncio

async with asyncio.timeout(30):
    result = await call_provider("Summarize this ticket")

When that outer deadline expires, your code receives a plain TimeoutError from asyncio, not a RelPrim error: RelPrim only reports its own deadline.

RelPrim’s timeout versus your client’s timeout

Only the expiry of the policy’s own deadline raises OperationTimeoutError. A TimeoutError raised by the operation itself, such as an HTTP client’s read timeout, propagates unchanged and is handled as an ordinary failure.

What happened Attempt failure Attempt status
The TimeoutPolicy deadline expired OperationTimeoutError (.timeout_seconds, .cause) timed_out
Your client raised TimeoutError before the deadline That TimeoutError failed

The distinction matters when deciding what to retry. With a narrowed retry_on, list both if both are transient for you:

from relprim import OperationTimeoutError, resilient


@resilient(retries=2, timeout=10, retry_on=(OperationTimeoutError, TimeoutError))
async def call_provider(prompt: str) -> str:
    return await provider.generate(prompt)

When the final attempt timed out, the report’s status is ExecutionStatus.TIMED_OUT and OperationExecutionError.cause is the OperationTimeoutError.

Cancellation

When the deadline expires, RelPrim cancels the running call and raises OperationTimeoutError for that attempt. When the caller is cancelled instead, asyncio.CancelledError propagates unchanged; it is never reported as a timeout.

Synchronous code

TimeoutPolicy is async only. Python cannot safely interrupt blocking synchronous code from the outside, so RelPrim does not pretend to. For synchronous clients, configure the timeout in the client itself and let RelPrim retry the exception it raises.