or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

channels.mdconfiguration.mderrors.mdfunctional-api.mdindex.mdmessage-graph.mdpregel.mdstate-graph.mdtypes-primitives.md

errors.mddocs/

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