RUNLOCALAIv38
->Will it run?Best GPUCompareTroubleshootStartLearnPulseModelsHardwareToolsBench
Run check
RUNLOCALAI

Independently operated catalog for local-AI hardware and software. Hand-written verdicts. Source-cited claims. Reproducible commands when we have them.

OP·Eruo Fredoline
DIR
  • Models
  • Hardware
  • Tools
  • Benchmarks
TOOLS
  • Will it run?
  • Compare hardware
  • Cost vs cloud
  • Choose my GPU
  • Prompting kits
  • Quick answers
REF
  • All buyer guides
  • Learn local AI
  • Methodology
  • Glossary
  • Errors KB
  • Trust
EDITOR
  • About
  • Author
  • How we make money
  • Editorial policy
  • Contact
LEGAL
  • Privacy
  • Terms
  • Sitemap
MAIL · MONTHLY DIGEST
Get monthly local AI changes
Monthly recap. No spam.
DISCLOSURE

Some links on this site are affiliate links (Amazon Associates and other first-class retailers). When you buy through them, we earn a small commission at no extra cost to you. Affiliate links do not influence our verdicts — there are cards we rate highly that we don't have affiliate relationships with, and cards that sell well that we refuse to recommend. Read more →

© 2026 runlocalai.coIndependently operated
RUNLOCALAI · v38
  1. >
  2. Home
  3. /Learn
  4. /Courses
  5. /LangGraph for Local Agents
  6. /Ch. 14
LangGraph for Local Agents

14. Error Handling

Chapter 14 of 18 · 15 min
KEY INSIGHT

LangGraph error handling is state-driven: catch exceptions, write error metadata to state, route to recovery via conditional edges. Bounded retries with escalation prevents infinite loops while giving transient failures a path to recover.

LangGraph does not have built-in retry logic by default. The standard approach is to implement error handling inside nodes or in a dedicated error-recovery node that the graph routes to after an error. The pattern: define an error: str | None field in the state schema, let nodes catch exceptions and populate that field, then use a conditional edge that routes to a recovery node when error is set.

def safe_researcher(state: TeamState) -> TeamState:
    try:
        result = risky_search(state["task"])
        findings = result["content"]
        return {"findings": findings, "error": None}
    except SearchError as e:
        return {"error": f"Search failed: {e}"}
    except TimeoutError as e:
        return {"error": f"Search timed out: {e}"}

def error_router(state: TeamState) -> Literal["retry_researcher", "escalate", END]:
    if state.get("retry_count", 0) >= 2:
        return "escalate"
    if state.get("error"):
        return "retry_researcher"
    return END

The retry counter increments in the recovery node. After N retries, the graph routes to escalate, which might send a notification or write an error to a log. This gives you bounded retries with an escalation path rather than infinite loops.

START → researcher → [if error] → retry_researcher → researcher
                                              ↓ (after 2 retries)
                                          escalate → END

For transient errors (network timeouts, rate limits), exponential backoff can be implemented by tracking last_attempt in state and comparing timestamps in the retry node's routing logic. For permanent errors (bad API keys, malformed input), skip retry and escalate immediately.

Local verification checkpoint

Run the smallest example from this chapter in a local workspace and record the package version, runtime, data path, and observed output. If the result depends on model size, vector count, CPU/GPU backend, or available memory, note that constraint beside the exercise so the lesson remains reproducible.

EXERCISE

Add a retry_count field to an existing node. Wrap the node's logic in try/except. Add a retry node that increments the counter and a conditional edge that escalates after two failures. Verify the graph correctly terminates the retry loop on the third attempt.

← Chapter 13
Subgraphs
Chapter 15 →
Persistence