error_kind instead of an opaque error string — so your agent can decide to retry, switch tools, or abort.
Backward compatible. All new parameters are optional. With no
timeout_ms and no cancel_token, execution delegates straight to the tool body — behaviour is unchanged.Quick Start
1
Add a hard deadline
Pass
timeout_ms (milliseconds) to execute_batch. A tool that runs longer is abandoned on a dedicated worker and returns a typed timeout result — the turn never hangs.2
Add a cancel token
Pass any The token is duck-typed:
threading.Event-like token. Signal it from another thread to short-circuit pending calls with a typed cancelled result.threading.Event (.is_set()) and InterruptController-like tokens (.is_cancelled / .cancelled) both work — no concrete import required.3
Handle structured errors
Pattern-match on
error_kind and read structured_error for a discriminated payload:How It Works
Because Python threads can’t be force-killed, a timed-out tool’s worker is abandoned (not joined) and a typed result is returned immediately — the turn keeps moving. Both executors forwardtimeout_ms and cancel_token:
error_kind Reference
ToolResult.error_kind is a discriminated tag; ToolResult.structured_error is None on success and a discriminated dict on failure.
Timeout, Cancel, or Both?
Config default via ToolExecutionConfig
Config default via ToolExecutionConfig
ToolExecutionConfig.timeout_ms is now consumed by the executor, so a configured default flows through to execute_batch without per-call wiring.Parallel batches
Parallel batches
ParallelToolCallExecutor enforces the same per-tool timeout inside each worker, so one hung tool resolves to a typed timeout result instead of blocking collection of the others.BaseTool compatibility
BaseTool compatibility
When PraisonAI wraps a framework tool with
tool_timeout, the resulting proxy is an instance of a dynamic subclass of the original tool’s class — so isinstance(proxy, BaseTool) (and any framework-specific base check used by CrewAI / LangChain / praisonaiagents.tool_execution for dispatch) still returns True. A BaseTool subclass with tool_timeout set on its owning agent executes normally through the same isinstance-routed .run path as an unwrapped tool; the wrapper adds a deadline but does not change dispatch. See PraisonAI Package Integration → Cross-generator tool isolation for the multi-generator behaviour.YAML tool_timeout precedence (multi-agent configs)
When you declare tool_timeout on agents or roles in YAML, PraisonAI resolves one effective per-tool timeout for the shared tool dict.
- CLI
tool_timeoutwins. An explicit value passed on the command line overrides everything. - Otherwise the tightest (
min) declared value applies across allroles:andagents:— here5.0s, not60. - Any agent whose declared value isn’t the tightest gets a warning naming it.
- Booleans are ignored — only
int/floatvalues count. If nothing declares a timeout, no timeout is applied.
This precedence applies to the YAML per-agent
tool_timeout field resolved for the shared tool dict. It is independent of the per-call timeout_ms argument to execute_batch documented above.Related
YAML Validation
Catch duplicate names and unknown task→agent references
Parallel Tool Calls
Run batched tool calls concurrently
Tool Progress
Stream incremental progress from slow tools
Deferred Tools
Hand back long-running work without blocking
Tools Overview
Build and register agent tools

