Timeouts, Retries & Reliability
Bound calls with timeouts and retry transient failures gracefully.
#Timeouts, Retries & Reliability
Set HTTP timeouts explicitly and retry only operations that are safe to repeat. A network timeout does not prove that remote work stopped or that a tool had no side effects.
#Per-call and per-tool timeouts
Client(timeout_seconds=...) controls the HTTPX timeout used for SDK requests. The default is 30 seconds. timeout= is an alias; supplying both with different values raises ValueError.
from maivn import Agent, Client
client = Client(api_key='...', timeout_seconds=60)
agent = Agent(name='reporter', client=client)For one invocation, use RunOptions(timeout=...):
from maivn import RunOptions
response = agent.invoke('Summarize today.', options=RunOptions(timeout=45))HTTPX distinguishes connection, read, write, and pool timeouts. A read timeout is an inactivity limit while waiting for data, not a wall-clock deadline for the whole run. Set deadlines on external services called by your tools as well.
#The four bounds
Only the HTTP setting below controls requests. The three execution settings are retained compatibility values; they do not impose tool or server-run deadlines.
#Resolution order
Per-call RunOptions.timeout overrides the client's HTTP timeout. Explicit constructor timeout values override corresponding ClientConfig fields; omitted values use the supplied configuration or SDK defaults. The active compatibility configuration context is not consulted automatically.
ClientBuilder.from_environment() reads credentials and plane URLs, but does not read timeout environment variables. See Configuration.
#When a timeout fires
import httpx
from maivn import Agent, Client
agent = Agent(name='reporter', client=Client(api_key='...', timeout_seconds=30))
try:
response = agent.invoke('Summarize today.')
except httpx.TimeoutException:
# Check the existing run before starting replacement work.
raiseUse client.cancel(session_id) or await client.acancel(session_id) to request cancellation when you have the session ID. Catching a timeout or closing a local stream is not itself confirmation that remote execution was cancelled.
#Retries and backoff for scheduled invocations
Retries in mAIvn are a property of a scheduled invocation, not of a one-off agent.invoke(...) call. The idea is simple: when you've asked an agent to run on a schedule, a single flaky fire shouldn't lose the whole slot. You attach a Retry policy and the scheduler reruns the fire — with a growing delay between attempts — before it gives up.
Pass a Retry to any schedule entry point (cron, interval, at):
from datetime import timedelta
import httpx
from maivn import Agent, Retry
from maivn.messages import HumanMessage
briefer = Agent(name='Daily briefing', api_key='...')
job = briefer.cron(
'23 9 * * MON-FRI', # weekdays at 09:23
tz='America/New_York',
mode='local',
retry=Retry(
max_attempts=4, # initial attempt + 3 retries
backoff='exponential',
base=timedelta(seconds=2),
factor=2.0,
max_delay=timedelta(seconds=30),
retry_on=(httpx.TimeoutException, httpx.ConnectError),
),
).invoke('Compose the daily ops briefing.')#The Retry policy
The backoff field is typed by the public RetryBackoff alias (one of 'constant', 'linear', 'exponential'). The delay before attempt n (1-indexed, so the first attempt has no delay) is:
- constant — always
base - linear —
base * (n - 1) - exponential —
base * factor ** (n - 2)
In every case the result is clamped to max_delay.
Tip: Prefer exponential backoff for anything that talks to a shared downstream service. A constant retry every two seconds against a service that's struggling tends to make the struggle worse; an exponential delay backs off out of its way and is far more likely to let it recover.
#Retry only what's worth retrying
retry_on is the most important field for correctness. Retrying a transient failure (a dropped connection, a timeout) is sensible. Retrying a deterministic failure (bad input, a misconfiguration) just wastes attempts and delays the inevitable — the second attempt will fail exactly like the first.
Scope retry_on to the transient cases:
import httpx
from maivn import Retry
retry = Retry(
max_attempts=3,
backoff='exponential',
retry_on=(httpx.TimeoutException, httpx.ConnectError), # transient only
)A ConfigurationError, ValueError, TypeError, or Pydantic validation failure is deterministic; fixing the config or data is the only path forward. The SDK does not expose an automatic is_retryable() helper. Classify the concrete failure and the safety of the operation in your application:
import httpx
if isinstance(error, (TimeoutError, ConnectionError, httpx.TimeoutException)):
schedule_a_retry()
else:
report_to_operator(error)#How retries interact with a single fire
Retries happen within a single fire of a scheduled job. A flaky run exhausts its attempts inside its own slot, while overlap policy controls how another scheduled fire is handled. Only the final outcome is recorded on the job's RunRecord. If every attempt fails, the job's on_error callback fires once, with the last exception:
job.on_error(lambda record: alert(record.error))Warning: A scheduled fire that exhausts its retries does not raise back into your code — the scheduler swallows it so it can keep running future fires. If you want to know about exhausted retries, you must wire on_error. Inspect job records and counters even if you do not install a callback.Retry is a dataclass and does not validate these limits at construction. Supply a positive attempt count, non-negative delay, and positive backoff factor. Check configuration values before constructing the policy.
For the full scheduling surface — cron expressions, jitter, misfire and overlap policies, and the ScheduledJob lifecycle — see the Scheduled Invocation guide and the Scheduling reference.
#Handling transient failures gracefully
Timeouts and retries reduce how often failures reach your code, but they don't eliminate them. The final piece of reliability is catching what does get through and responding deliberately.
The mechanics of which exceptions the SDK raises, where to import them from, and how to build a layered try/except around invoke / stream live in one place — the Errors & Exceptions reference. This guide doesn't repeat that surface; instead, here is the reliability lens on it.
Two facts carry most of the weight for reliability decisions:
- Catch the public hierarchy. Use
MaivnHTTPErrorfor typed API responses andMaivnSDKErrorfor other SDK-owned failures. - Retry only concrete transient failures. Configuration, input, and authorization failures do not improve on a second attempt. Connection and timeout failures may be retryable when the operation is safe.
import httpx
if isinstance(error, (TimeoutError, ConnectionError, httpx.TimeoutException)):
schedule_a_retry()
else:
report_to_operator(error)For typed API failures, log MaivnHTTPError.status_code, .code, .reason, and .detail. For other SDK failures, log the exception type and string. See Errors & Exceptions.
Note: If you stream agent activity to a browser, transient failures should reach the UI too, not just your logs — the same lifecycle stream that carries phase updates also carries error events. See the Frontend Events guide for the pattern.
#A reliability checklist
- Set HTTP and downstream timeouts. Do not treat compatibility execution settings as enforced run deadlines.
- Avoid duplicate effects. Confirm whether existing work completed before retrying a timed-out operation.
- Retry only the transient. Scope
retry_onto timeouts and connection errors; never retryConfigurationErrororValidationError. - Back off exponentially against any shared downstream service.
- Wire
on_erroron every scheduled job — exhausted retries never raise back into your code. - Catch
MaivnHTTPErrorandMaivnSDKErrorbefore broader transport exceptions so recovery can use the public fields. - Log typed HTTP fields (
status_code,code,reason,detail) and the concrete exception type.
#Next steps
- Scheduled Invocation — the full retry, jitter, misfire, and overlap model for cron / interval / one-shot jobs.
- Dependencies — how tool chaining works, which is how upstream results reach downstream tools.
- Errors & Exceptions — the canonical exception surface: every error type, where to import it, and the layered
try/exceptpatterns this guide builds on. - Frontend Events — stream lifecycle and error events to a browser via
mount_events. - Troubleshooting — symptom-first fixes for the most common errors, including timeouts and connection failures.
- Configuration reference — every timeout field and its environment variable.
- Client reference — the constructor signature and timeout resolution methods.
- Scheduling reference — the
Retry,JitterSpec,ScheduledJob, andRunRecordAPI. - Core Concepts and the Overview — the mental model the rest of the SDK builds on.