How to Fix 'state not updating' in LlamaIndex (Python)

By Cyprian AaronsUpdated 2026-04-21
state-not-updatingllamaindexpython

If you’re seeing state not updating in LlamaIndex, it usually means your workflow or agent is mutating state in a way LlamaIndex can’t observe. In practice, this shows up when you expect a tool call, step output, or workflow state object to change, but the framework keeps reading the old value.

This usually happens in Python when you mix immutable state patterns with in-place mutation, reuse stale objects across runs, or forget to return the updated state from a step.

The Most Common Cause

The #1 cause is mutating a copied or nested object and never returning the updated state from the workflow step. In LlamaIndex workflows, state updates need to flow through the framework’s state object, not just Python variables sitting inside your function.

Here’s the broken pattern:

from llama_index.core.workflow import Workflow, step, Context
from dataclasses import dataclass, field

@dataclass
class ChatState:
    messages: list[str] = field(default_factory=list)

class ChatWorkflow(Workflow):
    @step
    async def add_message(self, ctx: Context, msg: str) -> str:
        state = await ctx.get("state")
        state.messages.append(msg)   # mutated locally
        # forgot to write it back
        return "done"

And here’s the correct pattern:

from llama_index.core.workflow import Workflow, step, Context
from dataclasses import dataclass, replace, field

@dataclass(frozen=True)
class ChatState:
    messages: tuple[str, ...] = ()

class ChatWorkflow(Workflow):
    @step
    async def add_message(self, ctx: Context, msg: str) -> str:
        state = await ctx.get("state")
        new_state = replace(state, messages=state.messages + (msg,))
        await ctx.set("state", new_state)
        return "done"

Side by side:

BrokenFixed
state.messages.append(msg)new_state = replace(state, messages=...)
Mutates local object onlyWrites updated state back with ctx.set(...)
Easy to miss in async flowsExplicit and deterministic

The same issue often appears with BaseChatEngine, AgentRunner, or custom workflow steps where you assume a Python list append is enough. It isn’t if LlamaIndex is tracking state snapshots internally.

Other Possible Causes

1. You’re reusing an old Context or workflow instance

If you keep one workflow instance alive across requests and reuse the same context object, you can end up reading stale state.

workflow = ChatWorkflow()

# bad: shared context across requests
ctx = Context(workflow)

await workflow.run(ctx=ctx)
await workflow.run(ctx=ctx)

Fix by creating a fresh context per request:

workflow = ChatWorkflow()

async def handle_request():
    ctx = Context(workflow)
    return await workflow.run(ctx=ctx)

2. Your state model is mutable and nested updates are getting lost

If your state contains dicts or lists inside a dataclass, mutating nested fields may not trigger the update path you expect.

@dataclass
class State:
    metadata: dict[str, str] = field(default_factory=dict)

state.metadata["status"] = "done"  # easy to miss in tracked workflows

Prefer replacing the whole object:

new_metadata = {**state.metadata, "status": "done"}
new_state = replace(state, metadata=new_metadata)
await ctx.set("state", new_state)

3. A tool returns data but your agent never stores it in memory/state

This happens with FunctionTool, custom tools, or agent callbacks. The tool succeeds, but nothing updates because you only returned text.

from llama_index.core.tools import FunctionTool

def get_balance(account_id: str) -> str:
    return "Balance is $1200"

If you need persistence, explicitly write the result into memory or workflow state after the tool call.

4. You’re on a version with a known workflow/state bug

LlamaIndex has had breaking changes around workflows and state handling across releases. If your code looks right but state not updating still appears alongside errors like:

  • ValueError: State not found in Context
  • RuntimeError: Workflow step did not complete
  • AttributeError: 'NoneType' object has no attribute ...

check your installed version first.

pip show llama-index
pip install -U llama-index

Also make sure related packages are aligned if you use subpackages like:

  • llama-index-core
  • llama-index-llms-openai
  • llama-index-agent-openai

How to Debug It

  1. Print the state before and after every step

    • Log the exact object identity and content.
    • If id(state) stays the same but values don’t change where expected, you’re probably mutating without persisting.
  2. Check whether you’re using immutable or mutable models

    • If your workflow expects replacement semantics and you’re appending to lists/dicts in place, switch to frozen dataclasses or Pydantic models with explicit reassignment.
  3. Trace where ctx.set(...) actually happens

    • Search for every write path.
    • If no step calls await ctx.set("state", updated_state), that’s your bug.
  4. Reduce to one step

    • Strip the workflow down to one input and one mutation.
    • If it works there but fails in the full pipeline, another step is overwriting your update later.

A simple debug hook helps:

print("before:", state)
print("after:", new_state)
await ctx.set("state", new_state)
print("saved")

If “after” looks right but downstream steps still see old values, something else is resetting state between steps.

Prevention

  • Use immutable state objects where possible.
    • Frozen dataclasses or Pydantic models make update points obvious.
  • Treat every update as “create new state + persist it.”
    • Don’t rely on list append or dict mutation inside workflows.
  • Add regression tests around workflow transitions.
    • Test that each step changes exactly what it should and nothing else.

If you’re building agents for production systems like banking or insurance workflows, this pattern matters more than it looks. Silent stale-state bugs are worse than hard failures because they produce wrong answers without crashing.


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