Skip to main content
Every tool call is automatically protected by a circuit breaker that stops repeated failures from wasting time.
Sync and Async Parity: Circuit breaker protection applies uniformly to both sync and async tool execution paths. Parity was first delivered in MervinPraison/PraisonAI#4469, which wraps each async invocation through CircuitBreaker.acall(...) — event-loop safe, no time.sleep. MervinPraison/PraisonAI#4533 then added the _circuit_breaker_precheck(...) / _circuit_breaker_record(...) helpers for the pre-check and outcome recording. Both a raised exception inside the tool and a result dict carrying error count as breaker failures — but approval / permission / policy / guardrail denials do not. Earlier releases skipped the breaker on achat()/astart().
This tool-level circuit breaker is separate from the new LLM idle-timeout circuit breaker, which protects against LLM provider stalls during model calls.
Per-agent scoping: breakers are now keyed per agent instance (tool_{id(self)}_{function_name}), so two agents that expose same-named tools (e.g. search) no longer share one breaker — one agent’s failures can’t degrade the other. Per-agent breakers are auto-pruned from the registry when the Agent is garbage-collected (via weakref.finalize), so a reused instance id can’t inherit a stale OPEN breaker — agent.close() / aclose() merely reclaims that space earlier.
Retrieving a breaker by tool name alone no longer returns the runtime instance. Because the registry key is now tool_{id(agent)}_{function_name}, calling get_circuit_breaker("tool_my_tool") builds a fresh, disconnected breaker — not the one protecting live calls. Use get_circuit_breaker(f"tool_{id(agent)}_my_tool"), or enumerate this agent’s breakers with the Observability pattern below. Cleanup is covered in Agent Lifecycle Cleanup.
The user triggers a flaky tool; repeated failures open the breaker so later calls fail fast instead of looping on errors.

Quick Start

1

Works by default

Circuit breaker protection is automatically enabled for every tool call with zero configuration needed.
2

Detect open circuit

When a tool fails 5 times consecutively, subsequent calls return an error dictionary instead of calling the tool.
3

Tune or reset

Customize circuit breaker behavior or reset all breakers between test runs.

How It Works

The async path (achat() / astart()) performs the same OPEN → HALF_OPEN → CLOSED transitions through the shared helpers — _circuit_breaker_precheck runs the pre-check, _circuit_breaker_record records the outcome — so this diagram applies uniformly to chat/start and achat/astart. The mechanism is identical to sync, but delivered through CircuitBreaker.acall(...) rather than CircuitBreaker.call(...) so it never blocks the event loop (PR #4469). An error-dict result counts as a failure just like a raised exception — the async path surfaces it to the breaker through a _ToolFailure sentinel, mirroring the sync _ToolFailure wrapper — so five error-dict results open the breaker exactly as five raises would. Async workflows using asyncio.gather(...) get the same parity: one failing tool opens only its own per-agent breaker and short-circuits, so it won’t consume retries across every gathered task.

Configuration Options


What Does NOT Trip the Breaker

Circuit breakers ignore certain error types to avoid false positives:
  • approval_denied — user rejected the tool call
  • permission_denied — access control failure
  • approval_error — approval workflow error
  • policy_denied — policy engine deny
  • guardrail_denied — guardrail-blocked tool call
These exclusions apply to both the sync and async execution paths. On the async path they’re checked as keys on the result dict returned by the tool, matching the sync _ToolFailure wrapper’s exclusions — so a denial never counts toward opening the breaker on either path.

Lifecycle

Per-agent breakers are pruned from the global registry automatically when the Agent is garbage-collected — a weakref.finalize callback is registered when each breaker is created. This closes the CPython id()-reuse window without requiring agent.close() / agent.aclose() to be called explicitly. Calling agent.close() / agent.aclose() triggers _cleanup_circuit_breakers(), which removes every tool_{id(agent)}_* entry from the registry earlier and deterministically — keeping it bounded and preventing a reused id from inheriting a stale OPEN breaker. See Agent Lifecycle Cleanup for the full teardown story.

Async tools

A user calling an unreliable async tool via await agent.achat(...) sees the breaker open after five raised exceptions (or five error-dict results); further calls short-circuit with a circuit_open: True error dict, terminating the async retry loop immediately.
Breakers are per Agent instance (tool_{id(self)}_{function_name}), so two agents sharing a tool name never share a breaker — an OPEN breaker on agent_a never blocks agent_b.

Common Patterns

Enumerate the breakers that belong to a specific agent instance.
Retrieving a single breaker by name works too — just build the per-agent key:

Best Practices

Circuit breakers prevent cascading failures and protect system stability. Keep them enabled in production environments to ensure reliable agent operation.
Track circuit breaker statistics in your monitoring systems. Frequent openings indicate underlying tool reliability issues that need attention.
Per-agent breakers are auto-pruned when the Agent is collected (via weakref.finalize). Explicit agent.close() reclaims registry space immediately. reset_all_circuit_breakers() remains the sledgehammer for global tests that need a clean slate regardless of GC timing.
When handling circuit_open: true responses, provide clear user feedback about temporary tool unavailability and suggest retry timeframes or alternative approaches.
An async tool that raises (e.g. RuntimeError("upstream 503")) counts as a breaker failure just like an error-dict result — the async path records the failure inside its except block. Five raised failures in a row open the breaker so the sixth call short-circuits with circuit_open: True instead of hammering the flaky tool again.

Model Failover

Automatic LLM provider switching

Error Handling

Comprehensive error handling strategies