Back to Pydantic Ai

Timeouts

docs/timeouts.md

2.27.07.1 KB
Original Source

Timeouts

Bounding how long one step inside a run may take, and ending a run from inside a tool, are answered by separate mechanisms with separate failure modes. This page maps them. To stop a run that is already in flight, see Cancelling a Run.

Bounding how long a step takes

Each knob below bounds a different unit of work. None of them bounds the wall-clock duration of a whole run.

What you want to boundHow to set itWhat happens on expiry
A single model requesttimeout on [ModelSettings][pydantic_ai.settings.ModelSettings]The provider client raises; the run fails unless a FallbackModel or a transport retry handles it
A function tool callAgent(tool_timeout=...), or timeout= on an individual tool — see Tool TimeoutThe model receives a retry prompt 'Timed out after N seconds.', consuming that tool's retry budget. A def tool is not actually stopped: the deadline is enforced around the await, so the worker thread runs to completion
A hook functiontimeout= on the @hooks.on.* decorator[HookTimeoutError][pydantic_ai.capabilities.HookTimeoutError], which is an [AgentRunError][pydantic_ai.exceptions.AgentRunError] and aborts the run
Connecting to an MCP serverMCPToolset(init_timeout=...), default 5 secondsThe connection and initialize handshake fail
A single MCP requestMCPToolset(read_timeout=...), default 300 secondsThe request fails; under the default tool_error_behavior='retry' the model sees it as a retryable tool error
Total work done by a run[UsageLimits][pydantic_ai.usage.UsageLimits] — requests, tool calls, tokens, or cost — see Usage Limits[UsageLimitExceeded][pydantic_ai.exceptions.UsageLimitExceeded]
Wall-clock duration of a whole runNothing built in — wrap agent.run() in asyncio.timeout (Python 3.11+) or anyio.fail_after(), or cancel a [CancellationToken][pydantic_ai.CancellationToken] from a timerThe run is cancelled

Two of these need qualifying:

  • ModelSettings['timeout'] is applied per model class, not universally. The OpenAI, Anthropic, Google, Groq, and Mistral model classes forward it to their provider client, as do the model classes built on OpenAI's — CerebrasModel, OllamaModel, OpenRouterModel, ZaiModel, and the Bedrock Mantle models — which inherit the forwarding from [OpenAIChatModel][pydantic_ai.models.openai.OpenAIChatModel] / [OpenAIResponsesModel][pydantic_ai.models.openai.OpenAIResponsesModel]. Other model classes ignore the setting, and the timeout on the HTTP client they were built with applies instead. When Pydantic AI creates that client itself, it defaults to a 600-second total timeout with a 5-second connect timeout. Google and Mistral additionally reject an httpx.Timeout object and accept only a number of seconds.

    To bound a request on a model class that ignores the setting, configure the timeout where that provider actually takes one. Most providers accept your own http_client, but several don't: [XaiProvider][pydantic_ai.providers.xai.XaiProvider] takes a client-level timeout (or a preconfigured xai_client), [BedrockProvider][pydantic_ai.providers.bedrock.BedrockProvider] takes aws_read_timeout and aws_connect_timeout (or a preconfigured bedrock_client), and [HuggingFaceProvider][pydantic_ai.providers.huggingface.HuggingFaceProvider] rejects http_client outright in favor of hf_client.

  • Tool timeouts are enforced by [FunctionToolset][pydantic_ai.toolsets.FunctionToolset] only, and each toolset carries its own. Agent(tool_timeout=...) sets the default for tools you register on the agent — it does not reach into a FunctionToolset you constructed yourself and passed via toolsets=[...]. Give that toolset its own FunctionToolset(timeout=...), or set timeout= on the individual tools. Tools coming from an MCP server, an external toolset, or a custom [AbstractToolset][pydantic_ai.toolsets.AbstractToolset] read neither; bound those with the server-side or transport-level timeout instead.

If you enforce a deadline inside a tool body yourself, catch the TimeoutError and re-raise it as [ModelRetry][pydantic_ai.exceptions.ModelRetry] or [ToolFailed][pydantic_ai.exceptions.ToolFailed] rather than letting it escape. What happens to a bare TimeoutError depends on whether that tool has a timeout of its own:

  • No timeout on the tool or its toolset. It is an ordinary exception and propagates out of the agent run — unless a capability implements on_tool_execute_error, which can turn it into a replacement tool result or a ModelRetry.
  • A timeout is configured. The call runs inside anyio.fail_after(timeout), which signals expiry with TimeoutError too, so a TimeoutError you raised yourself is indistinguishable from the deadline expiring and becomes the same 'Timed out after N seconds.' retry prompt — reporting a deadline that may never have passed.

Re-raising in the tool is the more local choice; the hook is for applying one policy across every tool.

Ending a run from inside a tool

What a tool raises decides whether the run continues, and what the model gets to see:

RaiseRun continues?The model sees
[ModelRetry][pydantic_ai.exceptions.ModelRetry]YesA retry prompt asking it to correct the call — consumes that tool's retry budget
[ToolFailed][pydantic_ai.exceptions.ToolFailed]YesA failed tool result to adapt to — does not consume the retry budget
[ApprovalRequired][pydantic_ai.exceptions.ApprovalRequired] / [CallDeferred][pydantic_ai.exceptions.CallDeferred]Ends the run with a [DeferredToolRequests][pydantic_ai.tools.DeferredToolRequests] output, unless a [HandleDeferredToolCalls][pydantic_ai.capabilities.HandleDeferredToolCalls] handler resolves the call inlineNothing yet — see Deferred Tools
Any other exceptionNoBy default nothing — it propagates out of agent.run(). A capability implementing on_tool_execute_error sees it first and can return a replacement tool result or raise ModelRetry, letting the run continue

A tool can also end the run without raising, by calling [RunContext.cancel()][pydantic_ai.tools.RunContext.cancel] — the run ends with [RunCancelled][pydantic_ai.exceptions.RunCancelled] and the tool's return value is discarded. See Cancelling the Run from a Tool.

There is no exception that ends a run early with a successful output. To let a tool finish the run with a value, make that value the run's output: give the agent an output tool the model can call, or an output function that produces the result.