By Sagar Shankaran, Founder of CallSphere
Build resilient LangGraph workflows with try/except patterns in nodes, fallback conditional edges, configurable retry logic, and dead-end recovery strategies for production agent systems.
Key takeaways
Agent workflows interact with external systems — LLM APIs, databases, web services, file systems. Any of these can fail. API rate limits, network timeouts, malformed LLM outputs, and tool execution errors are not edge cases — they are normal operating conditions. Production LangGraph workflows must handle errors gracefully rather than crashing and losing all accumulated state.
The first line of defense is try/except blocks within node functions:
flowchart TD
USER(["User input"])
SUPER["Supervisor node<br/>routes by state"]
A["Specialist node A<br/>research"]
B["Specialist node B<br/>writing"]
TOOL{"Tool call<br/>needed?"}
EXEC["Tool executor<br/>ToolNode"]
CHK[("Postgres<br/>checkpointer")]
INT{"interrupt for<br/>human approval?"}
HUMAN(["Human reviewer"])
OUT(["Final response"])
USER --> SUPER
SUPER --> A
SUPER --> B
A --> TOOL
B --> TOOL
TOOL -->|Yes| EXEC --> SUPER
TOOL -->|No| INT
INT -->|Yes| HUMAN --> SUPER
INT -->|No| OUT
SUPER <--> CHK
style SUPER fill:#4f46e5,stroke:#4338ca,color:#fff
style CHK fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
style HUMAN fill:#f59e0b,stroke:#d97706,color:#1f2937
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
class State(TypedDict):
messages: Annotated[list, add_messages]
error: str
retry_count: int
llm = ChatOpenAI(model="gpt-4o-mini")
def call_llm(state: State) -> dict:
try:
response = llm.invoke(state["messages"])
return {
"messages": [response],
"error": "",
"retry_count": state.get("retry_count", 0),
}
except Exception as e:
return {
"error": str(e),
"retry_count": state.get("retry_count", 0) + 1,
}
By catching exceptions and writing error information to state, you keep the graph running and let downstream nodes or routing logic decide how to recover.
Use conditional edges to route to different nodes depending on whether an error occurred:
from typing import Literal
def check_error(state: State) -> Literal["retry", "fallback", "continue"]:
if state.get("error"):
if state.get("retry_count", 0) < 3:
return "retry"
return "fallback"
return "continue"
def retry_node(state: State) -> dict:
"""Wait briefly and clear the error for retry."""
import time
time.sleep(1) # Back off before retry
return {"error": ""}
def fallback_node(state: State) -> dict:
"""Provide a graceful degradation response."""
return {
"messages": [AIMessage(
content="I encountered an issue processing your request. "
"Here is what I can tell you based on available information."
)],
"error": "",
}
builder = StateGraph(State)
builder.add_node("agent", call_llm)
builder.add_node("retry", retry_node)
builder.add_node("fallback", fallback_node)
builder.add_node("respond", lambda s: s)
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", check_error, {
"retry": "retry",
"fallback": "fallback",
"continue": "respond",
})
builder.add_edge("retry", "agent") # Loop back for retry
builder.add_edge("fallback", END)
builder.add_edge("respond", END)
graph = builder.compile()
This pattern gives the agent three attempts before falling back to a graceful degradation response.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
For more sophisticated retry logic, implement exponential backoff:
import time
def smart_retry(state: State) -> dict:
count = state.get("retry_count", 0)
delay = min(2 ** count, 30) # 1s, 2s, 4s, 8s... max 30s
time.sleep(delay)
return {"error": ""}
This prevents overwhelming a failing service with rapid retries while still recovering quickly from transient errors.
Tools fail frequently — APIs return errors, queries time out, external services go down. Build error handling directly into your tools:
from langchain_core.tools import tool
import httpx
@tool
def fetch_data(url: str) -> str:
"""Fetch data from a URL with error handling."""
try:
response = httpx.get(url, timeout=10)
response.raise_for_status()
return response.text[:2000]
except httpx.TimeoutException:
return "ERROR: Request timed out. The server may be slow or unreachable."
except httpx.HTTPStatusError as e:
return f"ERROR: HTTP {e.response.status_code}. The resource may not exist."
except Exception as e:
return f"ERROR: {type(e).__name__}: {e}"
Returning error strings instead of raising exceptions lets the LLM see the error and decide how to proceed — perhaps by trying a different URL or rephrasing the query.
Sometimes the agent gets stuck in a loop without making progress. Detect this by tracking state changes:
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
def detect_stall(state: State) -> Literal["continue", "abort"]:
messages = state["messages"]
if len(messages) < 4:
return "continue"
# Check if last 3 AI messages are similar (stuck in a loop)
recent_ai = [
m.content for m in messages[-6:]
if isinstance(m, AIMessage)
][-3:]
if len(recent_ai) == 3 and len(set(recent_ai)) == 1:
return "abort"
return "continue"
def abort_node(state: State) -> dict:
return {
"messages": [AIMessage(
content="I appear to be stuck. Let me summarize what I have so far "
"and suggest a different approach."
)]
}
Checkpointing and error handling work together for maximum resilience:
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
graph = builder.compile(checkpointer=memory)
config = {"configurable": {"thread_id": "resilient-session"}}
try:
result = graph.invoke(
{"messages": [HumanMessage(content="Process this complex request")]},
config,
)
except Exception:
# Graph crashed — but state is checkpointed
# Resume from last successful node
result = graph.invoke(None, config)
Even if the entire process crashes, the checkpointed state lets you resume from the last successful node rather than restarting the entire workflow.
No. Catch exceptions that you can meaningfully handle — API errors, timeouts, validation failures. Let unexpected errors (programming bugs, out-of-memory) propagate so they surface during development rather than being silently swallowed.
Write errors to a separate state field like error_log that your response formatting node ignores. Alternatively, use Python logging within nodes to send error details to your observability stack while returning user-friendly messages to state.
LangGraph does not have a built-in global timeout. Implement it at the application level by running graph.ainvoke() inside an asyncio.wait_for() with your desired timeout. If the timeout triggers, the checkpointed state is still available for later resumption.
#LangGraph #ErrorHandling #RetryLogic #FaultTolerance #Python #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
How short-term (thread-scoped) and long-term (cross-thread) memory actually work in LangGraph, with code, schemas, and the eviction policies that keep cost predictable.
Langgraph multi-agent supervisor handoffs docs: the supervisor pattern in LangGraph for coordinating specialist agents, with full code, an eval pipeline that scores routing accuracy, and the failure modes to watch for.
How to stream tokens, tool-call deltas, and intermediate steps from an agent — with code for both the OpenAI Agents SDK and LangChain — and the gotchas that bite in production.
Build a browser agent with LangGraph and Playwright that does multi-step web tasks, then ground-truth its work with visual diffs and DOM-based evaluators.
Beyond single-shot RAG — agentic RAG with LangGraph that re-retrieves, self-grades, and rewrites queries. With evals that catch silent retrieval drift.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI