Error handling strategies for deep agents
Different error types require different handling strategies: Transient errors (network issues, rate limits) should be retried automatically with exponential backoff using ModelRetryMiddleware or ToolRetryMiddleware. LLM-recoverable errors (tool failures, parsing issues) should be converted to error ToolMessages so the model can adjust. User-fixable errors (missing information, unclear instructions) should pause with interrupt(). Provider outages should fall back to an alternative model using ModelFallbackMiddleware. Excessive calls (runaway loops) should be capped using ModelCallLimitMiddleware and ToolCallLimitMiddleware. Unexpected errors should be allowed to bubble up for debugging.
ModelRetryMiddleware configuration
ModelRetryMiddleware retries model calls on rate limits, timeouts, and 5xx errors. Parameters: max_retries (maximum number of retry attempts), backoff_factor (multiplier for delay between retries, e.g., 2.0), initial_delay (starting delay in seconds before first retry, e.g., 1.0). Example: ModelRetryMiddleware(max_retries=3, backoff_factor=2.0, initial_delay=1.0).
ToolRetryMiddleware configuration
ToolRetryMiddleware retries specific tool calls that fail with transient errors. Parameters: max_retries (maximum retry attempts), tools (list of specific tool names to retry, e.g., ['search', 'fetch_url']), retry_on (tuple of exception types to retry on, e.g., (TimeoutError, ConnectionError)). Should be scoped to specific tools rather than retrying all tools, as not all tool failures benefit from retry (e.g., filesystem read_file failures).
ToolErrorMiddleware converts tool exceptions to recoverable errors
ToolErrorMiddleware catches tool exceptions and converts them into error ToolMessages so the LLM can see what went wrong and retry with adjusted inputs. Requires langchain>=1.3.14. Takes an on_error handler function that accepts exception and ToolCallRequest, returns a string message for recoverable errors or None to propagate unexpected errors. Example: if isinstance(exc, ValueError), return error message; otherwise return None to propagate.
ModelFallbackMiddleware for provider outages
ModelFallbackMiddleware automatically switches to an alternative model if the primary model provider goes down entirely. Takes the fallback model identifier as a string parameter. Example: ModelFallbackMiddleware('gpt-5.5').
ModelCallLimitMiddleware and ToolCallLimitMiddleware
ModelCallLimitMiddleware caps the number of model calls per run to prevent runaway loops burning through API budgets. ToolCallLimitMiddleware caps the number of tool executions per run. Both accept run_limit parameter to cap calls within a single invocation (resets each turn), and thread_limit parameter to cap calls across an entire conversation (requires a checkpointer). Example: ModelCallLimitMiddleware(run_limit=50), ToolCallLimitMiddleware(run_limit=200).
InMemoryRateLimiter for provider rate limiting
InMemoryRateLimiter controls the rate at which requests are sent to model providers. Parameters: requests_per_second (e.g., 0.1 for 1 request every 10 seconds), check_every_n_seconds (how often to check if allowed to make a request, e.g., 0.1 for every 100ms), max_bucket_size (maximum burst size allowed). Initialize the model with rate_limiter parameter: init_chat_model(model='...', rate_limiter=rate_limiter).
Middleware composition for fault tolerance
Multiple middleware can be composed together in the middleware list to handle different fault tolerance strategies simultaneously. Example: create_deep_agent(model='...', middleware=[ModelRetryMiddleware(...), ToolErrorMiddleware(...), ModelCallLimitMiddleware(...)]).
Python retry middleware example with search and fetch tools
This example shows creating a deep agent with both ModelRetryMiddleware and ToolRetryMiddleware: from deepagents import create_deep_agent
from langchain.agents.middleware import ModelRetryMiddleware, ToolRetryMiddleware
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
middleware=[
ModelRetryMiddleware(max_retries=3, backoff_factor=2.0, initial_delay=1.0),
ToolRetryMiddleware(
max_retries=2,
tools=["search", "fetch_url"],
retry_on=(TimeoutError, ConnectionError),
),
],
)
JavaScript retry middleware example
This example shows creating an agent with retry middleware in JavaScript: import {
createAgent,
modelRetryMiddleware,
toolRetryMiddleware,
} from "langchain";
const agent = createAgent({
model: "google_genai:gemini-3.6-flash",
middleware: [
modelRetryMiddleware({ maxRetries: 3, backoffFactor: 2.0, initialDelayMs: 1000 }),
toolRetryMiddleware({
maxRetries: 2,
tools: ["search", "fetch_url"],
retryOn: [TimeoutError, TypeError],
}),
],
});
Python ToolErrorMiddleware example
This example shows using ToolErrorMiddleware to catch ValueError exceptions: from deepagents import create_deep_agent
from langchain.agents.middleware import ToolErrorMiddleware
def on_error(exc: Exception, request: ToolCallRequest) -> str | None:
if isinstance(exc, ValueError):
return f"`{request.tool_call['name']}` failed with {type(exc).__name__}."
# propagate everything else
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
middleware=[ToolErrorMiddleware(on_error)],
)
Python ModelFallbackMiddleware example
This example shows configuring a fallback model: from deepagents import create_deep_agent
from langchain.agents.middleware import ModelFallbackMiddleware
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
middleware=[
ModelFallbackMiddleware("gpt-5.5"),
],
)
Python ModelCallLimitMiddleware and ToolCallLimitMiddleware example
This example shows capping model and tool calls per run: from deepagents import create_deep_agent
from langchain.agents.middleware import ModelCallLimitMiddleware, ToolCallLimitMiddleware
agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
middleware=[
ModelCallLimitMiddleware(run_limit=50),
ToolCallLimitMiddleware(run_limit=200),
],
)
Python InMemoryRateLimiter example
This example shows initializing a model with rate limiting: from langchain.rate_limiters import InMemoryRateLimiter
from langchain.chat_models import init_chat_model
from deepagents import create_deep_agent
rate_limiter = InMemoryRateLimiter(
requests_per_second=0.1,
check_every_n_seconds=0.1,
max_bucket_size=10,
)
model = init_chat_model(
model="google_genai:gemini-3.6-flash",
rate_limiter=rate_limiter,
)
agent = create_deep_agent(model=model, tools=[search_tool])
JavaScript ModelFallbackMiddleware example
This example shows configuring a fallback model in JavaScript: import {
createAgent,
modelFallbackMiddleware,
} from "langchain";
const agent = createAgent({
model: "google_genai:gemini-3.6-flash",
middleware: [
modelFallbackMiddleware("gpt-5.5"),
],
});
JavaScript ModelCallLimitMiddleware and ToolCallLimitMiddleware example
This example shows capping model and tool calls per run in JavaScript: import { createAgent, modelCallLimitMiddleware, toolCallLimitMiddleware } from "langchain";
const agent = createAgent({
model: "google_genai:gemini-3.6-flash",
middleware: [
modelCallLimitMiddleware({ runLimit: 50 }),
toolCallLimitMiddleware({ runLimit: 200 }),
],
});
Pitfall: Not scoping ToolRetryMiddleware to specific tools
Do not retry all tools indiscriminately. Scope ToolRetryMiddleware to specific tools that hit external APIs. A filesystem read_file that fails will not benefit from a retry, but a web search that times out probably will. Specify tools=['search', 'fetch_url'] rather than retrying every tool.
Pitfall: Runaway loops burning API budget without call limits
Without call limits, a confused agent can burn through LLM API budget in minutes by looping on the same tool call or making hundreds of model calls. Always set ModelCallLimitMiddleware and ToolCallLimitMiddleware caps, especially run_limit to cap calls within a single invocation.
Durable execution with checkpoints in LangGraph
Deep Agents run on LangGraph which provides durable execution out of the box. The persistence layer checkpoints state at each step, so a run interrupted by failure, timeout, or human-in-the-loop pause resumes from its last recorded state without reprocessing previous steps. For long-running deep agents that spawn many subagents, mid-run failure doesn't lose completed work. Checkpointing enables indefinite interrupts (human-in-the-loop workflows can pause for minutes or days and resume exactly where they left off), time travel (every checkpointed step is a snapshot you can rewind to for replay if something goes wrong), and safe handling of sensitive operations (workflows involving payments or irreversible actions get audit trails and recovery points).
Avoid passing secrets to sandboxes via environment variables or file uploads
Avoid passing secrets into sandboxes via environment variables or file uploads. Agents can read any accessible file or environment variable inside the sandbox, including credentials. The sandbox auth proxy keeps secrets out of the sandbox entirely.