0
# Error Handling
1
2
LangGraph provides a comprehensive set of exception classes for handling various error conditions during graph execution. These exceptions enable robust error handling, proper error reporting, and control flow management.
3
4
## Imports
5
6
```python
7
from langgraph.errors import (
8
GraphRecursionError,
9
InvalidUpdateError,
10
GraphBubbleUp,
11
GraphInterrupt,
12
NodeInterrupt,
13
ParentCommand,
14
EmptyInputError,
15
TaskNotFound,
16
EmptyChannelError,
17
ErrorCode,
18
create_error_message
19
)
20
21
# Warning classes (from langgraph.warnings, not langgraph.errors)
22
from langgraph.warnings import (
23
LangGraphDeprecationWarning,
24
LangGraphDeprecatedSinceV05,
25
LangGraphDeprecatedSinceV10
26
)
27
```
28
29
## Capabilities
30
31
### Exception Classes
32
33
#### GraphRecursionError
34
35
Raised when a graph exhausts its maximum number of steps.
36
37
```python { .api }
38
class GraphRecursionError(RecursionError):
39
"""
40
Raised when graph execution exhausts max steps.
41
42
This occurs when the graph runs for more steps than the configured
43
recursion_limit. The default limit is typically set in the config.
44
45
Usage:
46
try:
47
result = app.invoke(input, config={"recursion_limit": 10})
48
except GraphRecursionError as e:
49
print(f"Graph exceeded step limit: {e}")
50
51
To increase the limit:
52
result = app.invoke(input, config={"recursion_limit": 100})
53
"""
54
```
55
56
#### InvalidUpdateError
57
58
Raised when a channel receives an invalid update.
59
60
```python { .api }
61
class InvalidUpdateError(Exception):
62
"""
63
Raised on invalid channel update.
64
65
Occurs when a node returns an update that cannot be applied to a
66
channel, such as wrong type or incompatible value.
67
68
Common causes:
69
- Returning wrong type for a channel
70
- Missing required state fields
71
- Incompatible reducer operations
72
73
Usage:
74
try:
75
result = app.invoke(input)
76
except InvalidUpdateError as e:
77
print(f"Invalid state update: {e}")
78
"""
79
```
80
81
#### EmptyChannelError
82
83
Raised when attempting to read from an empty channel.
84
85
```python { .api }
86
class EmptyChannelError(Exception):
87
"""
88
Raised when accessing an empty channel.
89
90
Occurs when a node tries to read from a channel that has no value.
91
This is re-exported from langgraph.checkpoint.base.
92
93
Usage:
94
try:
95
value = channel.get()
96
except EmptyChannelError:
97
print("Channel is empty")
98
"""
99
```
100
101
#### EmptyInputError
102
103
Raised when a graph receives empty input.
104
105
```python { .api }
106
class EmptyInputError(Exception):
107
"""
108
Raised when graph receives empty input.
109
110
Occurs when invoke() is called with None or empty input when the
111
graph expects actual input data.
112
113
Usage:
114
try:
115
result = app.invoke(None)
116
except EmptyInputError as e:
117
print(f"Graph requires input: {e}")
118
"""
119
```
120
121
#### TaskNotFound
122
123
Raised when the executor cannot find a task.
124
125
```python { .api }
126
class TaskNotFound(Exception):
127
"""
128
Raised when executor cannot find task.
129
130
Internal error that occurs when the execution engine tries to
131
execute a task that doesn't exist in the graph.
132
133
This typically indicates a bug in graph construction or internal
134
execution logic.
135
"""
136
```
137
138
#### GraphBubbleUp
139
140
Internal exception for control flow (not typically caught by users).
141
142
```python { .api }
143
class GraphBubbleUp(Exception):
144
"""
145
Internal exception for control flow.
146
147
Base class for exceptions that bubble up through the execution stack
148
to implement control flow patterns. Not typically caught by user code.
149
150
This is used internally for implementing features like interrupts
151
and parent commands.
152
"""
153
```
154
155
#### GraphInterrupt
156
157
Raised internally when a subgraph is interrupted.
158
159
```python { .api }
160
class GraphInterrupt(GraphBubbleUp):
161
"""
162
Raised when subgraph is interrupted.
163
164
Internal exception that propagates interrupt signals through the
165
execution stack. Extends GraphBubbleUp.
166
167
User code should use the interrupt() function instead of raising
168
this exception directly.
169
"""
170
```
171
172
#### NodeInterrupt (Deprecated)
173
174
Deprecated in v1.0.0 - use the `interrupt()` function instead.
175
176
```python { .api }
177
class NodeInterrupt(GraphInterrupt):
178
"""
179
DEPRECATED in v1.0.0 - Use interrupt() function instead.
180
181
Previously used to interrupt execution from within a node.
182
Now replaced by the interrupt() function which provides better
183
ergonomics and resume support.
184
185
Migration:
186
# Old way (deprecated)
187
raise NodeInterrupt("approval needed")
188
189
# New way
190
from langgraph.types import interrupt
191
response = interrupt("approval needed")
192
"""
193
```
194
195
#### ParentCommand
196
197
Internal exception for parent graph commands.
198
199
```python { .api }
200
class ParentCommand(GraphBubbleUp):
201
"""
202
Internal exception for parent commands.
203
204
Used internally to propagate Command objects targeting parent graphs
205
through the execution stack.
206
207
User code returns Command objects; this exception is raised internally
208
by the execution engine.
209
"""
210
```
211
212
### Error Codes
213
214
Enumeration of error codes for troubleshooting.
215
216
```python { .api }
217
from enum import Enum
218
219
class ErrorCode(Enum):
220
"""
221
Error codes for troubleshooting.
222
223
Provides standardized error codes that link to documentation
224
for common error conditions.
225
226
Enum Values:
227
GRAPH_RECURSION_LIMIT: Graph exceeded recursion limit
228
INVALID_CONCURRENT_GRAPH_UPDATE: Concurrent update conflict
229
INVALID_GRAPH_NODE_RETURN_VALUE: Node returned invalid value
230
MULTIPLE_SUBGRAPHS: Multiple subgraphs in invalid context
231
INVALID_CHAT_HISTORY: Invalid chat history format
232
233
Usage:
234
from langgraph.errors import ErrorCode, create_error_message
235
236
message = create_error_message(
237
message="Graph exceeded step limit",
238
error_code=ErrorCode.GRAPH_RECURSION_LIMIT
239
)
240
"""
241
242
GRAPH_RECURSION_LIMIT = "GRAPH_RECURSION_LIMIT"
243
INVALID_CONCURRENT_GRAPH_UPDATE = "INVALID_CONCURRENT_GRAPH_UPDATE"
244
INVALID_GRAPH_NODE_RETURN_VALUE = "INVALID_GRAPH_NODE_RETURN_VALUE"
245
MULTIPLE_SUBGRAPHS = "MULTIPLE_SUBGRAPHS"
246
INVALID_CHAT_HISTORY = "INVALID_CHAT_HISTORY"
247
```
248
249
### Warning Classes
250
251
**Note:** Warning classes are exported from `langgraph.warnings`, not `langgraph.errors`.
252
253
LangGraph provides deprecation warning classes for tracking deprecated functionality.
254
255
```python { .api }
256
class LangGraphDeprecationWarning(DeprecationWarning):
257
"""
258
Base deprecation warning for LangGraph.
259
260
Note: Import from langgraph.warnings, not langgraph.errors
261
262
Attributes:
263
message: str - Description of the warning
264
since: tuple[int, int] - Version where deprecated (major, minor)
265
expected_removal: tuple[int, int] - Version for removal
266
267
Usage:
268
import warnings
269
from langgraph.warnings import LangGraphDeprecationWarning
270
271
warnings.filterwarnings("default", category=LangGraphDeprecationWarning)
272
"""
273
274
message: str
275
since: tuple[int, int]
276
expected_removal: tuple[int, int]
277
278
class LangGraphDeprecatedSinceV05(LangGraphDeprecationWarning):
279
"""
280
Functionality deprecated since LangGraph v0.5.0.
281
282
Expected removal in v2.0.0.
283
"""
284
285
class LangGraphDeprecatedSinceV10(LangGraphDeprecationWarning):
286
"""
287
Functionality deprecated since LangGraph v1.0.0.
288
289
Expected removal in v2.0.0.
290
291
Examples of v1.0.0 deprecations:
292
- MessageGraph (use StateGraph with MessagesState)
293
- NodeInterrupt (use interrupt() function)
294
- Importing Send/Interrupt from langgraph.constants
295
"""
296
```
297
298
### Error Utilities
299
300
#### create_error_message Function
301
302
Creates a formatted error message with troubleshooting link.
303
304
```python { .api }
305
def create_error_message(*, message: str, error_code: ErrorCode) -> str:
306
"""
307
Create formatted error message with troubleshooting link.
308
309
Generates a user-friendly error message that includes a link to
310
documentation for the specific error code.
311
312
Parameters:
313
message: str - The error message text
314
error_code: ErrorCode - The error code for linking to docs
315
316
Returns:
317
str - Formatted error message with documentation link
318
319
Usage:
320
from langgraph.errors import ErrorCode, create_error_message
321
322
error_msg = create_error_message(
323
message="Node returned invalid value type",
324
error_code=ErrorCode.INVALID_GRAPH_NODE_RETURN_VALUE
325
)
326
raise ValueError(error_msg)
327
"""
328
```
329
330
## Usage Examples
331
332
### Handling Recursion Errors
333
334
```python
335
from langgraph.graph import StateGraph, START, END
336
from langgraph.errors import GraphRecursionError
337
338
class State(TypedDict):
339
count: int
340
341
def increment(state: State) -> dict:
342
return {"count": state["count"] + 1}
343
344
def should_continue(state: State) -> str:
345
# Infinite loop without termination condition
346
return "increment"
347
348
graph = StateGraph(State)
349
graph.add_node("increment", increment)
350
graph.add_edge(START, "increment")
351
graph.add_conditional_edges("increment", should_continue, {"increment": "increment"})
352
353
app = graph.compile()
354
355
try:
356
result = app.invoke({"count": 0}, config={"recursion_limit": 10})
357
except GraphRecursionError as e:
358
print(f"Graph exceeded limit: {e}")
359
# Handle by increasing limit or fixing termination condition
360
result = app.invoke({"count": 0}, config={"recursion_limit": 100})
361
```
362
363
### Handling Invalid Updates
364
365
```python
366
from typing import TypedDict
367
from langgraph.graph import StateGraph, START, END
368
from langgraph.errors import InvalidUpdateError
369
370
class State(TypedDict):
371
value: int # Expects int
372
373
def bad_node(state: State) -> dict:
374
# Returns string instead of int
375
return {"value": "not an int"}
376
377
graph = StateGraph(State)
378
graph.add_node("bad", bad_node)
379
graph.add_edge(START, "bad")
380
graph.add_edge("bad", END)
381
382
app = graph.compile()
383
384
try:
385
result = app.invoke({"value": 0})
386
except InvalidUpdateError as e:
387
print(f"Invalid update: {e}")
388
# Fix the node to return correct type
389
```
390
391
### Handling Empty Channel Errors
392
393
```python
394
from typing import TypedDict
395
from langgraph.graph import StateGraph, START, END
396
from langgraph.errors import EmptyChannelError
397
398
class State(TypedDict):
399
required_field: str
400
optional_field: str
401
402
def node_reading_optional(state: State) -> dict:
403
try:
404
# Try to read optional field
405
value = state.get("optional_field", "default")
406
return {"required_field": value}
407
except EmptyChannelError:
408
return {"required_field": "default"}
409
410
graph = StateGraph(State)
411
graph.add_node("reader", node_reading_optional)
412
graph.add_edge(START, "reader")
413
graph.add_edge("reader", END)
414
415
app = graph.compile()
416
```
417
418
### Using Error Codes for Custom Errors
419
420
```python
421
from langgraph.errors import ErrorCode, create_error_message
422
423
def validate_graph_structure(graph):
424
"""Validate graph and raise descriptive errors."""
425
if graph.has_cycles_without_exit:
426
error_msg = create_error_message(
427
message="Graph contains cycle without exit condition. "
428
"This will cause infinite execution.",
429
error_code=ErrorCode.GRAPH_RECURSION_LIMIT
430
)
431
raise ValueError(error_msg)
432
433
if graph.has_invalid_return_types:
434
error_msg = create_error_message(
435
message="Node returns value incompatible with state schema",
436
error_code=ErrorCode.INVALID_GRAPH_NODE_RETURN_VALUE
437
)
438
raise TypeError(error_msg)
439
```
440
441
### Handling Interrupts
442
443
```python
444
from langgraph.graph import StateGraph, START, END
445
from langgraph.errors import GraphInterrupt
446
from langgraph.types import interrupt
447
from langgraph.checkpoint.memory import MemorySaver
448
449
class State(TypedDict):
450
data: str
451
approved: bool
452
453
def approval_node(state: State) -> dict:
454
# Use interrupt() function (not GraphInterrupt exception)
455
response = interrupt("Need approval for: " + state["data"])
456
return {"approved": response == "yes"}
457
458
graph = StateGraph(State)
459
graph.add_node("approval", approval_node)
460
graph.add_edge(START, "approval")
461
graph.add_edge("approval", END)
462
463
checkpointer = MemorySaver()
464
app = graph.compile(checkpointer=checkpointer)
465
466
config = {"configurable": {"thread_id": "1"}}
467
468
# First run - execution interrupted
469
try:
470
result = app.invoke({"data": "important", "approved": False}, config)
471
except GraphInterrupt:
472
# This shouldn't be caught - interrupt() handles it internally
473
pass
474
475
# Check state and resume
476
state = app.get_state(config)
477
if state.interrupts:
478
from langgraph.types import Command
479
result = app.invoke(Command(resume="yes"), config)
480
```
481
482
### Comprehensive Error Handling
483
484
```python
485
from langgraph.graph import StateGraph, START, END
486
from langgraph.errors import (
487
GraphRecursionError,
488
InvalidUpdateError,
489
EmptyInputError,
490
EmptyChannelError
491
)
492
from langgraph.checkpoint.memory import MemorySaver
493
494
class State(TypedDict):
495
data: str
496
result: str
497
498
def process(state: State) -> dict:
499
if not state.get("data"):
500
raise ValueError("Data is required")
501
return {"result": state["data"].upper()}
502
503
graph = StateGraph(State)
504
graph.add_node("process", process)
505
graph.add_edge(START, "process")
506
graph.add_edge("process", END)
507
508
checkpointer = MemorySaver()
509
app = graph.compile(checkpointer=checkpointer)
510
511
def safe_invoke(input_data, config):
512
"""Wrapper with comprehensive error handling."""
513
try:
514
return app.invoke(input_data, config)
515
516
except EmptyInputError as e:
517
print(f"Missing input: {e}")
518
return {"error": "Input required"}
519
520
except InvalidUpdateError as e:
521
print(f"Invalid state update: {e}")
522
return {"error": "Invalid update"}
523
524
except GraphRecursionError as e:
525
print(f"Recursion limit exceeded: {e}")
526
# Retry with higher limit
527
return app.invoke(input_data, {**config, "recursion_limit": 200})
528
529
except EmptyChannelError as e:
530
print(f"Missing required state: {e}")
531
return {"error": "Missing state"}
532
533
except ValueError as e:
534
print(f"Validation error: {e}")
535
return {"error": str(e)}
536
537
except Exception as e:
538
print(f"Unexpected error: {e}")
539
return {"error": "Internal error"}
540
541
# Use safe wrapper
542
config = {"configurable": {"thread_id": "1"}}
543
result = safe_invoke({"data": "test"}, config)
544
```
545
546
### Custom Error Handling with Retry
547
548
```python
549
from typing import TypedDict
550
from langgraph.graph import StateGraph, START, END
551
from langgraph.types import RetryPolicy
552
553
class State(TypedDict):
554
attempts: int
555
result: str
556
557
class TransientError(Exception):
558
"""Custom transient error that should be retried."""
559
pass
560
561
def unreliable_node(state: State) -> dict:
562
"""Node that might raise transient errors."""
563
if state["attempts"] < 2:
564
raise TransientError("Temporary failure")
565
return {"result": "success", "attempts": state["attempts"] + 1}
566
567
# Create graph with retry policy for transient errors
568
graph = StateGraph(State)
569
graph.add_node(
570
"work",
571
unreliable_node,
572
retry_policy=RetryPolicy(
573
max_attempts=3,
574
initial_interval=1.0,
575
retry_on=TransientError # Only retry on TransientError
576
)
577
)
578
graph.add_edge(START, "work")
579
graph.add_edge("work", END)
580
581
app = graph.compile()
582
583
# TransientError will be retried automatically
584
try:
585
result = app.invoke({"attempts": 0, "result": ""})
586
print(f"Success: {result}")
587
except TransientError as e:
588
print(f"Failed after retries: {e}")
589
```
590
591
## Notes
592
593
- Most exceptions should not be caught directly - let them propagate for proper error reporting
594
- `GraphRecursionError` can be resolved by increasing `recursion_limit` in config
595
- `InvalidUpdateError` typically indicates a type mismatch in state updates
596
- Use `interrupt()` function instead of raising `NodeInterrupt` (deprecated)
597
- Internal exceptions (`GraphBubbleUp`, `ParentCommand`) are for framework use only
598
- Error codes provide links to detailed troubleshooting documentation
599
- Retry policies can automatically handle specific exception types
600
- Always use checkpointer when implementing interrupt-based workflows
601