Documentation Index
Fetch the complete documentation index at: https://docs.statebase.org/llms.txt
Use this file to discover all available pages before exploring further.
Agent Failure Modes
AI agents fail in ways that traditional software doesn’t. Understanding these failure modes is essential to building resilient production systems. StateBase is designed to help you detect, debug, and recover from each of these patterns.
The 7 Deadly Failures
1. Hallucination Corruption
What happens: The LLM generates plausible-sounding but completely false information, which gets stored in state or memory.
# User: "What's my account balance?"
# Agent (hallucinating): "Your balance is $1,234.56"
# Reality: User has never provided account info
# State gets corrupted:
state = {"account_balance": 1234.56} # ❌ Completely made up
Detection:
# Check if state contains unverified data
if "account_balance" in state and not state.get("balance_verified"):
alert("Potential hallucination: unverified balance in state")
Recovery with StateBase:
# Roll back to before the hallucination
sb.sessions.rollback(session_id=session.id, version=previous_version)
# Add a safety check
sb.sessions.update_state(
session_id=session.id,
state={"requires_verification": True},
reasoning="Detected unverified financial data"
)
Prevention:
- Never trust LLM output for critical data (balances, medical info, legal advice)
- Always verify with authoritative sources (APIs, databases)
- Use structured output (JSON schema validation) to catch hallucinations early
2. Context Overflow
What happens: The conversation history grows too large, causing:
- Increased latency (slow responses)
- Increased cost (more tokens)
- Context window limits (truncation)
# After 50 turns, context is 100,000 tokens
context = sb.sessions.get_context(
session_id=session.id,
turn_limit=50 # ❌ Too many turns
)
# LLM call fails: "Context exceeds 128k token limit"
Detection:
# Monitor context size
turn_count = len(sb.sessions.list_turns(session_id=session.id))
if turn_count > 30:
alert("Session approaching context limit")
Recovery with StateBase:
# Option 1: Summarize old turns and start fresh
summary = llm.summarize(old_turns)
sb.memory.add(
content=summary,
type="conversation_summary",
session_id=session.id
)
# Create new session with summary
new_session = sb.sessions.create(
agent_id=agent_id,
user_id=user_id,
initial_state={"previous_summary": summary}
)
# Option 2: Use sliding window (only last N turns)
context = sb.sessions.get_context(
session_id=session.id,
turn_limit=10 # ✅ Only recent turns
)
Prevention:
- Set turn limits in
get_context() (10-20 is usually enough)
- Summarize periodically (every 20 turns)
- Use memory for long-term facts (don’t rely on turn history)
3. State Drift
What happens: The agent’s internal state diverges from reality because external systems changed.
# Agent state: {"order_status": "pending"}
# Reality: Order was cancelled by user in another app
# Agent continues as if order is still pending
# User: "Why hasn't my order shipped?"
# Agent: "Your order is still pending, should ship soon" ❌
Detection:
# Periodically sync state with source of truth
actual_status = get_order_status_from_db(order_id)
if state["order_status"] != actual_status:
alert("State drift detected")
Recovery with StateBase:
# Update state with authoritative data
sb.sessions.update_state(
session_id=session.id,
state={"order_status": actual_status, "synced_at": now()},
reasoning="Synced with order database"
)
Prevention:
- Sync state with external systems before critical operations
- Use TTLs on cached data (e.g.,
{"weather": {...}, "cached_until": timestamp})
- Validate state against source of truth periodically
What happens: The agent calls an external API (weather, database, payment processor) and it fails.
# Agent tries to call weather API
try:
weather = call_weather_api("San Francisco")
except APIError:
# ❌ Agent doesn't know how to handle this
# Conversation breaks
Detection:
# Log all tool call failures
try:
result = call_external_api()
except Exception as e:
sb.sessions.add_turn(
session_id=session.id,
input={"type": "tool_call", "tool": "weather_api"},
output={"type": "error", "message": str(e)},
reasoning=f"Tool call failed: {e}"
)
Recovery with StateBase:
# Checkpoint before tool call
sb.sessions.update_state(
session_id=session.id,
state={"pre_tool_call_state": current_state},
reasoning="Checkpoint before weather API call"
)
# Try tool call
try:
result = call_weather_api()
sb.sessions.update_state(
session_id=session.id,
state={"weather": result},
reasoning="Weather API call succeeded"
)
except APIError:
# Roll back to pre-call state
sb.sessions.rollback(session_id=session.id, version=previous_version)
# Inform user gracefully
return "I'm having trouble accessing weather data right now. Can I help with something else?"
Prevention:
- Always checkpoint before risky operations
- Implement retry logic (with exponential backoff)
- Have fallback responses for common failures
5. Infinite Loops
What happens: The agent gets stuck repeating the same action over and over.
# Turn 10: "Let me search for that..."
# Turn 11: "Let me search for that..."
# Turn 12: "Let me search for that..."
# ❌ Agent is stuck in a loop
Detection:
# Check for repeated outputs
recent_turns = sb.sessions.list_turns(session_id=session.id, limit=5)
outputs = [turn.output.content for turn in recent_turns]
if len(set(outputs)) == 1: # All outputs are identical
alert("Infinite loop detected")
Recovery with StateBase:
# Detect loop
if is_looping(session.id):
# Roll back to before loop started
sb.sessions.rollback(session_id=session.id, version=loop_start_version)
# Add loop prevention flag
sb.sessions.update_state(
session_id=session.id,
state={"loop_detected": True, "attempted_action": "search"},
reasoning="Detected infinite loop, preventing retry"
)
# Try different approach
return "I'm having trouble with that search. Let me try a different approach."
Prevention:
- Track action history in state (e.g.,
{"attempted_actions": ["search", "search"]})
- Limit retries (max 3 attempts per action)
- Use circuit breakers (stop trying after repeated failures)
6. Permission Violations
What happens: The agent attempts an action the user isn’t authorized for.
# User: "Delete all customer data"
# Agent: "Sure, deleting..." ❌ DANGER!
# Agent should check permissions first
if not user.has_permission("delete_customer_data"):
return "You don't have permission for that action"
Detection:
# Log all permission checks
if not has_permission(user_id, action):
sb.traces.create(
session_id=session.id,
action="permission_denied",
details={"user_id": user_id, "attempted_action": action}
)
Recovery with StateBase:
# If agent violates permissions, roll back immediately
if permission_violation_detected:
sb.sessions.rollback(session_id=session.id, version=safe_version)
# Flag session for review
sb.sessions.update_state(
session_id=session.id,
state={"flagged_for_review": True, "reason": "permission_violation"},
reasoning="Agent attempted unauthorized action"
)
# Notify security team
alert_security(session.id)
Prevention:
- Implement permission checks before every destructive operation
- Use allowlists (explicitly define what agent CAN do, not what it can’t)
- Require human confirmation for high-risk actions
7. Memory Pollution
What happens: The agent stores incorrect or irrelevant information in long-term memory.
# User (joking): "I hate pizza"
# Agent stores: "User hates pizza" ❌
# Later, in a different session:
# Agent: "I know you hate pizza, so I won't recommend Italian restaurants"
# User: "What? I love pizza!"
Detection:
# Review memories periodically
memories = sb.memory.search(query="user preferences", session_id=session.id)
# Flag low-confidence memories
for memory in memories:
if memory.metadata.get("confidence") < 0.7:
alert("Low-confidence memory detected")
Recovery with StateBase:
# Delete incorrect memory
sb.memory.delete(memory_id=incorrect_memory.id)
# Add corrected memory
sb.memory.add(
content="User loves pizza",
type="preference",
session_id=session.id,
metadata={"confidence": 1.0, "corrected": True}
)
Prevention:
- Confirm before storing preferences (“Just to confirm, you prefer X?”)
- Use confidence scores (only store high-confidence facts)
- Allow users to review/edit memories (via dashboard)
Failure Detection Framework
Implement this monitoring system to catch failures early:
class FailureDetector:
def __init__(self, session_id):
self.session_id = session_id
self.alerts = []
def check_hallucination(self, state):
"""Detect unverified critical data"""
critical_fields = ["account_balance", "medical_diagnosis", "legal_advice"]
for field in critical_fields:
if field in state and not state.get(f"{field}_verified"):
self.alerts.append(f"Unverified {field} detected")
def check_context_overflow(self):
"""Detect excessive turn count"""
turn_count = len(sb.sessions.list_turns(session_id=self.session_id))
if turn_count > 30:
self.alerts.append(f"Context overflow: {turn_count} turns")
def check_infinite_loop(self):
"""Detect repeated actions"""
recent_turns = sb.sessions.list_turns(session_id=self.session_id, limit=5)
outputs = [turn.output.content for turn in recent_turns]
if len(set(outputs)) == 1:
self.alerts.append("Infinite loop detected")
def check_tool_failures(self):
"""Detect repeated tool call failures"""
traces = sb.traces.list(session_id=self.session_id, action="tool_call.failed")
if len(traces) > 3:
self.alerts.append(f"Multiple tool failures: {len(traces)}")
def run_all_checks(self, state):
"""Run all failure detection checks"""
self.check_hallucination(state)
self.check_context_overflow()
self.check_infinite_loop()
self.check_tool_failures()
if self.alerts:
notify_team(self.alerts)
return len(self.alerts) == 0 # True if healthy
Recovery Decision Tree
When a failure is detected, use this decision tree:
Failure Detected
├─ Is it recoverable?
│ ├─ Yes → Roll back to last good state
│ └─ No → Escalate to human
│
├─ Is it a systemic issue?
│ ├─ Yes → Alert engineering team
│ └─ No → Log and continue
│
└─ Can we prevent recurrence?
├─ Yes → Update agent logic
└─ No → Add to monitoring
Best Practices
✅ Do This
- Monitor for all 7 failure modes (use the detection framework above)
- Checkpoint before risky operations (tool calls, state updates)
- Implement graceful degradation (fallback responses when tools fail)
- Log everything (you’ll need it for debugging)
- Test failure scenarios (inject failures in staging)
❌ Avoid This
- Don’t ignore repeated failures (they indicate systemic issues)
- Don’t trust LLM output blindly (always validate critical data)
- Don’t let sessions grow unbounded (summarize or split)
- Don’t skip permission checks (security > convenience)
Testing Failure Modes
Inject failures in your test environment to verify recovery:
# Test hallucination recovery
def test_hallucination_recovery():
session = sb.sessions.create(agent_id="test-agent")
# Inject hallucinated data
sb.sessions.update_state(
session_id=session.id,
state={"account_balance": 99999.99}, # Fake data
reasoning="Test: Injected hallucination"
)
# Verify detection
detector = FailureDetector(session.id)
assert not detector.check_hallucination(session.state)
# Verify recovery
sb.sessions.rollback(session_id=session.id, version=0)
assert "account_balance" not in sb.sessions.get(session.id).state
Next Steps
Key Takeaway: AI agents will fail. The question is whether you can detect and recover fast enough. StateBase gives you the tools to turn failures into learning opportunities.