Paste-match catalog of 14 real LangChain 1.0 / LangGraph 1.0 exceptions with named
causes and named fixes, plus a triage decision tree. Use when you have a traceback
and want the specific fix, not speculative documentation. Covers ImportError (0.2/0.3 → 1.0
migration), AttributeError on AIMessage.content, KeyError in LCEL and prompts,
GraphRecursionError, silent thread_id memory loss, JSON-serialization crashes,
and graphs that halt without reaching END. Trigger with "langchain error",
"langgraph traceback", "OutputParserException", "GraphRecursionError",
"ImportError langchain", "AttributeError AIMessage content".
Paste-match catalog of 14 real LangChain 1.0 / LangGraph 1.0 exceptions with named
causes and named fixes, plus a triage decision tree. Use when you have a traceback
and want the specific fix, not speculative documentation. Covers ImportError (0.2/0.3 → 1.0
migration), AttributeError on AIMessage.content, KeyError in LCEL and prompts,
GraphRecursionError, silent thread_id memory loss, JSON-serialization crashes,
and graphs that halt without reaching END. Trigger with "langchain error",
"langgraph traceback", "OutputParserException", "GraphRecursionError",
"ImportError langchain", "AttributeError AIMessage content".
Designed for Claude Code, also compatible with Codex
LangChain Common Errors (Python)
Overview
The same twelve-plus LangChain 1.0 / LangGraph 1.0 tracebacks show up every week
in production: ImportError: cannot import name 'ChatOpenAI' from 'langchain.chat_models',
AttributeError: 'list' object has no attribute 'lower', GraphRecursionError: Recursion limit of 25 reached, TypeError: Object of type datetime is not JSON serializable, KeyError: 'question' deep in LCEL internals. Stack traces are
ambiguous, docs sprawl across 0.2 / 0.3 / 1.0 eras, and engineers lose
30–90 minutes per incident re-deriving the fix.
This skill is a paste-match catalog of 14 entries (E01–E14) grouped into
3 reference files by category, plus a triage decision tree. Every entry opens
with the exact exception class and message string you see in your terminal,
names the cause in one sentence with a pain-catalog code, and gives a one-line
fix or a reference-file pointer. Pinned to langchain-core 1.0.x,
langchain 1.0.x, langgraph 1.0.x, verified 2026-04-21.
A traceback or a reproducible bug — this skill is diagnostic, not preventive
langchain-core >= 1.0, < 2.0, langgraph >= 1.0, < 2.0, and any provider
integration packages pinned to 1.0.x
anthropic >= 0.42 when using langchain-anthropic 1.0 (see E06)
Instructions
Triage first. Read the first line of the traceback. Look up the exception
class in the catalog below or in Triage Decision Tree.
Match the message string, not the call site. Each catalog entry opens with
the literal message pattern you see.
Apply the named fix. If the entry points to a reference file, read that
file for the deep walk-through including codemods, before/after code, and
adjacent traps. Otherwise use the one-line fix here.
Verify with a test. Every catalog entry names a test that will catch the
error in CI next time.
If no match: check docs/pain-catalog.md for the exception class name or
a message substring. Do not add speculative fixes here without catalog evidence.
Catalog
Each entry is indexed as E## — ClassName: "message pattern" with a one-line
cause (pain-catalog code) and a one-line fix or reference pointer.
Fix:pip install "langchain-anthropic>=1.0,<2.0" "anthropic>=0.42,<1.0" in the same commit. See E06 in import-migration.md.
Category B — Content-shape & prompt-template errors
Full detail, block-iterator extractor, jinja2 escape pattern: content-shape.md.
E07 — AttributeError: 'list' object has no attribute 'lower' (or .strip, .split, .format)
Cause:AIMessage.content is list[dict] on Claude with any non-text block and on OpenAI once tools are bound (P02).
Fix: Use msg.text() (LangChain 1.0+) or iterate and filter block["type"] == "text". See E07 in content-shape.md.
E08 — KeyError: '<var>' inside runnables/passthrough.py or runnables/base.py
Cause: LCEL dict-shape mismatch between runnable stages; error surfaces at the consumer, not the producer (P06).
Fix: Insert RunnableLambda debug probes between stages and enable set_debug(True). See E08 in content-shape.md.
E09 — KeyError: '<var>' inside prompts/chat.py or prompts/prompt.py on user input
Cause:ChatPromptTemplate.from_messages defaults to f-string parsing; user-provided { / } characters are parsed as template variables (P57).
Fix: Use MessagesPlaceholder("history") for variable content, or template_format="jinja2" for free-text templates, or escape literals as {{ / }}. See E09 in content-shape.md.
Category C — Agent & graph execution traps
Full detail, diagnostic callbacks, thread_id middleware, router asserts: graph-traps.md.
E10 — GraphRecursionError: Recursion limit of 25 reached without hitting a stop condition
Cause:create_react_agent and StateGraph.compile() default recursion_limit=25 counts supersteps, not loop iterations; vague prompts never converge (P10, P55).
Fix: Set recursion_limit=10 for interactive use; add terminal edge on repeated tool-call names. See E10 in graph-traps.md.
E11 — AgentExecutor returns "I couldn't find the answer" on every tool call, no exception
Cause:handle_parsing_errors=True catches tool exceptions and passes str(exc) (often empty) as observation; loop continues without signal (P09).
Fix:return_intermediate_steps=True, handle_parsing_errors=False or migrate to LangGraph create_react_agent. See E11 in graph-traps.md.
E12 — Multi-turn chat forgets everything between calls, no exception
Cause: LangGraph checkpointers key state by config["configurable"]["thread_id"]; omitted thread_id means every invocation gets a fresh state with no warning (P16).
Fix: Require thread_id at app boundary; wrap in middleware that raises on missing key. See E12 in graph-traps.md.
E13 — TypeError: Object of type <datetime / bytes / Decimal / set> is not JSON serializable
Cause:InMemorySaver / PostgresSaver / SqliteSaver serialize state as JSON on every superstep; non-primitives break at interrupt or checkpoint boundary (P17).
Fix: Keep state JSON-primitive only (str/int/float/bool/list/dict); serialize at node boundaries; or use JsonPlusSerializer for Pydantic v2. See E13 in graph-traps.md.
E14 — Graph halts without reaching END, no exception, no log
Cause:add_conditional_edges(node, router, path_map) where router returns a string not in path_map causes silent termination on some 1.0 versions (P56).
Fix:assert router_return in path_map; always include END explicitly; inspect graph.get_state(cfg).next. See E14 in graph-traps.md.
Output
Named cause tied to a P## pain-catalog code for every traceback
Named fix (one-line or a reference-file walk-through) tied to E## catalog entry
A regression test pattern that catches the error in CI next time
Triage path recorded for the incident retro (which entry matched, how long it took)
Error Handling
This skill is the error-handling reference for the pack. The triage workflow:
First line of traceback → pick category in the table below.
Exception class + message substring → pick E## entry above.
Reference file → read the deep walk-through for before/after code and tests.
First-line pattern
Category
Entries
Reference
ImportError from langchain.*
A
E01, E03, E04
import-migration.md
AttributeError on langchain.* module
A
E02
import-migration.md
AttributeError on AgentAction / ToolCall
A
E05
import-migration.md
ImportError / KeyError against langchain-anthropic
A
E06
import-migration.md
AttributeError on list.lower / .strip
B
E07
content-shape.md
KeyError in runnables/
B
E08
content-shape.md
KeyError in prompts/
B
E09
content-shape.md
GraphRecursionError
C
E10
graph-traps.md
Silent wrong answers / empty tool errors
C
E11
graph-traps.md
Multi-turn memory loss, no exception
C
E12
graph-traps.md
TypeError ... not JSON serializable
C
E13
graph-traps.md
Graph halts without reaching END
C
E14
graph-traps.md
For tracebacks that do not match: consult triage-decision-tree.md
for the full routing flowchart, then docs/pain-catalog.md for any remaining
pain codes. Escalate to the main thread before adding speculative fixes.
Examples
Example 1 — ImportError on a 0.3 → 1.0 upgrade
Incoming traceback:
Traceback (most recent call last):
File "app.py", line 3, in <module>
from langchain.chat_models import ChatOpenAI
ImportError: cannot import name 'ChatOpenAI' from 'langchain.chat_models'
Triage:
First line is ImportError from langchain.chat_models → Category A.
Re-run tests. Next failure is almost always E07 (AIMessage.content shape)
once the imports resolve — walk that one next from content-shape.md.
Example 2 — GraphRecursionError triage
Incoming traceback:
Traceback (most recent call last):
File "worker.py", line 88, in run
result = agent.invoke({"messages": [("user", q)]}, config=cfg)
...
langgraph.errors.GraphRecursionError: Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the `recursion_limit` config key.
Triage:
Exception class is GraphRecursionError → Category C.
Matches E10 exactly.
Cause: vague prompt or tool loop; default recursion_limit=25 (P10, P55).
Before raising the limit, diagnose first — attach a StepLogger callback
(see graph-traps.md § E10) and count
real steps. If the same two node names alternate, it is a convergence bug,
not a budget bug.
def_should_stop(state):
recent = [m for m in state["messages"][-6:] ifgetattr(m, "tool_calls", None)]
iflen(recent) >= 3andall(
r.tool_calls[0]["name"] == recent[0].tool_calls[0]["name"] for r in recent
):
return"end"return"continue"
graph.add_conditional_edges("agent", _should_stop, {"continue": "agent", "end": END})
Pair with a per-session token-budget middleware from langchain-cost-tuning.