How to Fix 'state not updating when scaling' in LangGraph (Python)

By Cyprian AaronsUpdated 2026-04-21
state-not-updating-when-scalinglanggraphpython

When LangGraph says your state is not updating when scaling, it usually means the graph is running, but the values you expect to accumulate across nodes are being overwritten, dropped, or isolated per branch. This shows up a lot when you move from a single-node prototype to parallel branches, loops, or multi-step workflows in Python.

The root issue is almost always state schema design: the graph is updating state exactly as defined, but your fields are not marked for accumulation, or you’re mutating state in a way LangGraph does not track.

The Most Common Cause

The #1 cause is using plain dict fields for values that should accumulate across nodes. In LangGraph, if multiple nodes write to the same key and you do not define a reducer, later writes overwrite earlier ones.

A common symptom is seeing state updates in one node, then losing them after branching or scaling out with StateGraph.

Broken patternFixed pattern
Plain list/dict assignment overwrites prior valuesUse Annotated[...] with a reducer like operator.add
Node mutates input state directlyNode returns a partial update dict
No merge strategy for concurrent writesState schema defines how updates combine

Broken code

from typing import TypedDict
from langgraph.graph import StateGraph, END

class AgentState(TypedDict):
    messages: list[str]

def node_a(state: AgentState):
    return {"messages": ["A"]}

def node_b(state: AgentState):
    return {"messages": ["B"]}

graph = StateGraph(AgentState)
graph.add_node("a", node_a)
graph.add_node("b", node_b)
graph.set_entry_point("a")
graph.add_edge("a", "b")
graph.add_edge("b", END)

app = graph.compile()
result = app.invoke({"messages": []})
print(result)

This looks fine until you scale to multiple branches or repeated writes. The last writer wins.

Fixed code

from typing import Annotated, TypedDict
import operator
from langgraph.graph import StateGraph, END

class AgentState(TypedDict):
    messages: Annotated[list[str], operator.add]

def node_a(state: AgentState):
    return {"messages": ["A"]}

def node_b(state: AgentState):
    return {"messages": ["B"]}

graph = StateGraph(AgentState)
graph.add_node("a", node_a)
graph.add_node("b", node_b)
graph.set_entry_point("a")
graph.add_edge("a", "b")
graph.add_edge("b", END)

app = graph.compile()
result = app.invoke({"messages": []})
print(result)
# {'messages': ['A', 'B']}

If your error appears during parallel execution, you may also see behavior that looks like this in logs:

  • InvalidUpdateError: At key 'messages': Can receive only one value per step
  • InvalidConcurrentGraphUpdateError
  • state keys appearing empty after a branch rejoins

That’s usually a reducer problem, not a runtime bug.

Other Possible Causes

1. You are mutating state in place

LangGraph expects nodes to return updates, not edit shared objects and hope they persist.

# Wrong
def summarize(state):
    state["summary"] = "done"
    return {}

# Right
def summarize(state):
    return {"summary": "done"}

If you mutate nested objects in place, especially lists and dicts, the graph may not detect the change reliably across steps.


2. Your state type does not match what nodes return

If your schema says int but your node returns a string or list, updates can fail silently until execution hits validation boundaries.

from typing import TypedDict

class State(TypedDict):
    count: int

def bad_node(state: State):
    return {"count": ["1"]}  # wrong type

Fix it by keeping the schema and returned payload aligned:

def good_node(state: State):
    return {"count": 1}

If you use Pydantic models or TypedDict, keep field names exact. A typo like message vs messages looks like “state not updating” because you’re writing to a different key.


3. You forgot to define reducers for parallel branches

This becomes visible when two nodes write to the same key in the same step. LangGraph can raise an error instead of guessing how to merge values.

from typing import Annotated, TypedDict
import operator

class State(TypedDict):
    logs: Annotated[list[str], operator.add]

Without that reducer, parallel fan-out often leads to errors such as:

  • InvalidConcurrentGraphUpdateError
  • InvalidUpdateError: At key 'logs': Can receive only one value per step

Use reducers for:

  • lists of messages/logs/events
  • counters that should increment
  • sets of collected outputs

4. You are using checkpointing or threads incorrectly

If you scale by adding persistence and thread IDs, each thread gets its own state history. If you reuse the wrong thread_id, or omit it when resuming, it looks like state reset itself.

config = {"configurable": {"thread_id": "user-123"}}
app.invoke({"messages": []}, config=config)

Common failure mode:

  • one request uses "user-123"
  • another request accidentally uses "default"
  • resume logic reads an empty thread and assumes updates were lost

Also check your checkpointer wiring:

from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)

Without a checkpointer, nothing persists across separate invocations.

How to Debug It

  1. Print the exact state at each node
    • Add logging inside every node.
    • Confirm whether the value disappears before or after the node returns.
def debug_node(state):
    print("INPUT:", state)
    update = {"messages": ["debug"]}
    print("OUTPUT:", update)
    return update
  1. Check whether multiple nodes write to the same key

    • If yes, add a reducer.
    • If no reducer exists and branches converge, expect InvalidUpdateError.
  2. Verify your schema types

    • Compare returned keys against your TypedDict or Pydantic model.
    • Look for wrong field names and wrong value types.
  3. Test with a single linear path first

    • Remove fan-out/fan-in temporarily.
    • If it works linearly but fails when branching, you have a merge/reducer issue.

Prevention

  • Define reducers up front for any field that will be appended to across steps:

    • messages
    • logs
    • tool results
    • audit trails
  • Keep nodes pure:

    • take state in
    • return partial updates out
    • do not mutate shared objects in place
  • Add one integration test that runs:

    • linear flow
    • branched flow
    • resumed flow with checkpointing

That catches most “state not updating when scaling” failures before they hit production.


Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides