Skip to main content
AgentDbg guardrails give you an automatic circuit breaker for the most common ways an agent run goes wrong: infinite loops, runaway token spend, and runs that simply take too long. They are designed for local debugging loops — every guardrail fires only when you opt in, records full trace evidence before aborting, and raises a typed exception so your code can respond deliberately.

What guardrails do

When a configured threshold is crossed, AgentDbg:
1

Records evidence

Writes the event or warning that triggered the guardrail using the normal trace format, so you have full context in the timeline.
2

Raises a typed exception

Raises AgentDbgLoopAbort (for loop detection) or AgentDbgGuardrailExceeded (for count and duration limits).
3

Finalizes the run

Records an ERROR event, then writes RUN_END with status="error" so the run is closed cleanly.
The ERROR payload includes guardrail, threshold, actual, error_type, and message — so the UI and raw trace both tell you not just that the run failed, but which guardrail fired and what the actual value was.

Available guardrails

ParameterTypeDefaultWhat it does
stop_on_loopboolFalseAbort when loop detection emits LOOP_WARNING
stop_on_loop_min_repetitionsint3Minimum repeated pattern count required before stop_on_loop fires
max_llm_callsint | NoneNoneAbort after more than N LLM calls
max_tool_callsint | NoneNoneAbort after more than N tool calls
max_eventsint | NoneNoneAbort after more than N total events
max_duration_sfloat | NoneNoneAbort when elapsed run time reaches this limit
Count-based guardrails (max_llm_calls, max_tool_calls, max_events) trigger at N+1, not N — the event that crosses the limit is recorded before the run is aborted. max_duration_s triggers when elapsed time is greater than or equal to the configured value.

Exceptions

AgentDbgLoopAbort

Raised when stop_on_loop=True and loop detection fires above the stop_on_loop_min_repetitions threshold. This is a subclass of AgentDbgGuardrailExceeded, so you can catch either.
from agentdbg import AgentDbgLoopAbort, trace

@trace(stop_on_loop=True)
def run_agent():
    ...

try:
    run_agent()
except AgentDbgLoopAbort as exc:
    print(f"Stopped because of a loop: {exc}")
    print(f"  guardrail: {exc.guardrail}")  # "stop_on_loop"
    print(f"  threshold: {exc.threshold}")  # minimum repetitions
    print(f"  actual:    {exc.actual}")     # observed repetition count

AgentDbgGuardrailExceeded

Raised for all count and duration guardrails. Exposes the following attributes:
AttributeTypeDescription
guardrailstrWhich guardrail fired, e.g. "max_llm_calls"
thresholdint | floatThe configured limit
actualint | floatThe value that exceeded the limit
messagestrHuman-readable description
from agentdbg import AgentDbgGuardrailExceeded, traced_run

try:
    with traced_run(max_llm_calls=8, max_tool_calls=12):
        ...
except AgentDbgGuardrailExceeded as exc:
    print(exc.guardrail, exc.threshold, exc.actual)

How guardrails appear in traces

Loop guardrail

When stop_on_loop=True and the loop detector fires:
  1. AgentDbg writes LOOP_WARNING
  2. Raises AgentDbgLoopAbort
  3. Writes ERROR
  4. Writes RUN_END(status="error")

Count and duration guardrails

For max_llm_calls, max_tool_calls, max_events, and max_duration_s:
  1. AgentDbg writes the event that crossed the limit (e.g. the LLM call that put you at N+1)
  2. Raises AgentDbgGuardrailExceeded
  3. Writes ERROR
  4. Writes RUN_END(status="error")
This means you always have full evidence of the specific step that tripped the guardrail when you open the timeline.

Configuration

Guardrails can be configured in four places. The highest-precedence source wins:
  1. Arguments passed directly to @trace(...) or traced_run(...)
  2. Environment variables
  3. Project YAML: .agentdbg/config.yaml
  4. User YAML: ~/.agentdbg/config.yaml

Decorator and context manager

from agentdbg import trace, traced_run

@trace(stop_on_loop=True, max_llm_calls=50)
def guarded_fn():
    ...

with traced_run(stop_on_loop=True, max_llm_calls=50):
    ...

Environment variables

export AGENTDBG_STOP_ON_LOOP=1
export AGENTDBG_STOP_ON_LOOP_MIN_REPETITIONS=3
export AGENTDBG_MAX_LLM_CALLS=50
export AGENTDBG_MAX_TOOL_CALLS=50
export AGENTDBG_MAX_EVENTS=200
export AGENTDBG_MAX_DURATION_S=60

YAML config

# .agentdbg/config.yaml or ~/.agentdbg/config.yaml
guardrails:
  stop_on_loop: true
  stop_on_loop_min_repetitions: 3
  max_llm_calls: 50
  max_tool_calls: 50
  max_events: 200
  max_duration_s: 60

Examples

Stop a looping agent immediately

from agentdbg import AgentDbgLoopAbort, record_llm_call, record_tool_call, trace


@trace(stop_on_loop=True)
def run_agent():
    for _ in range(10):
        record_tool_call("search_db", args={"q": "pricing"}, result={"hits": 3})
        record_llm_call(model="gpt-4.1", prompt="Summarize", response="Retrying...")


try:
    run_agent()
except AgentDbgLoopAbort as exc:
    print(f"Stopped because of a loop: {exc}")

Cap LLM and tool usage during development

from agentdbg import AgentDbgGuardrailExceeded, record_llm_call, record_tool_call, traced_run


try:
    with traced_run(
        name="react_debug",
        max_llm_calls=8,
        max_tool_calls=12,
        max_events=40,
        max_duration_s=30,
    ):
        # ... your agent loop ...
        record_llm_call(model="gpt-4.1", prompt="...", response="...")
        record_tool_call(name="search", args={"q": "docs"}, result={"hits": 2})
except AgentDbgGuardrailExceeded as exc:
    print(exc.guardrail, exc.threshold, exc.actual)
Practical starting points for local development:
  • stop_on_loop=True for any ReAct-style or planner/executor loop
  • max_llm_calls=10 to 30 when iterating on prompts
  • max_tool_calls=10 to 25 for tool-heavy debugging sessions
  • max_events=50 to 200 for a hard ceiling on trace file size
  • max_duration_s=15 to 60 for runs that should finish quickly
Use tighter limits in tests and demos, and looser limits when you are intentionally exploring a larger workflow.