langchain-ai/deepagents · error · RuntimeError

StateBackend requires CONFIG_KEY_READ / CONFIG_KEY_SEND in t

Error message

StateBackend requires CONFIG_KEY_READ / CONFIG_KEY_SEND in the LangGraph config. Make sure the backend is used inside a graph node or tool, not called directly. To pre-populate files, pass them on invoke: agent.invoke({"messages": [...], "files": {...}})

What it means

`StateBackend` reads and writes the `files` channel through LangGraph Pregel internals using two reserved config keys (`CONFIG_KEY_READ` / `CONFIG_KEY_SEND`). `_get_config` raises this `RuntimeError` when a config exists but lacks these keys — i.e. the code is running inside LangChain but not inside a real graph node/tool where LangGraph injects them. It signals the backend is being called from a non-graph LangChain context such as a plain Runnable chain or direct tool invocation.

Source

Thrown at libs/deepagents/deepagents/backends/state.py:78

            config = get_config()
        except RuntimeError:
            msg = (
                "StateBackend must be used inside a LangGraph graph execution "
                "(e.g. via create_deep_agent). It cannot read or write state "
                "outside of a graph context. To pre-populate files, pass them "
                'on invoke: agent.invoke({"messages": [...], "files": {...}})'
            )
            raise RuntimeError(msg) from None
        configurable = config.get("configurable", {})
        if CONFIG_KEY_READ not in configurable:
            msg = (
                "StateBackend requires CONFIG_KEY_READ / CONFIG_KEY_SEND in "
                "the LangGraph config. Make sure the backend is used inside "
                "a graph node or tool, not called directly. To pre-populate "
                "files, pass them on invoke: "
                'agent.invoke({"messages": [...], "files": {...}})'
            )
            raise RuntimeError(msg)
        return config

    def _read_files(self) -> dict[str, Any]:
        """Read the current `files` channel via Pregel internals.

        Uses `CONFIG_KEY_READ` to read state directly — this lets us
        initialize StateBackend once and fetch state on demand from any
        graph context (tools, middleware nodes, etc.).

        `fresh=True` applies any pending task writes through the channel's
        reducer before returning, giving read-your-writes semantics within
        a single superstep — e.g. a tool that writes a file and then reads
        it back, or a code interpreter that issues multiple sub-tool calls
        inside one eval.
        """
        config = self._get_config()
        read = config["configurable"][CONFIG_KEY_READ]
        fresh = True

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Run the backend within a LangGraph graph node or tool (via `create_deep_agent`) so LangGraph injects CONFIG_KEY_READ/CONFIG_KEY_SEND
  2. In unit tests, use the repo's state-backend test fixtures that build a fake config containing both keys
  3. Pass files on invoke (`agent.invoke({'messages': [...], 'files': {...}})`) instead of touching the backend directly
  4. Use a non-state backend if you need standalone file I/O

Example fix

// before
backend = StateBackend()
backend.read(['/a.txt'], config={'configurable': {}})  # RuntimeError

// after
agent = create_deep_agent(backend=StateBackend(), tools=[...])
result = agent.invoke({'messages': [{'role': 'user', 'content': 'read /a.txt'}]})
Defensive patterns

Strategy: validation

Validate before calling

def config_has_state_keys(config) -> bool:
    conf = (config or {}).get('configurable', {})
    return CONFIG_KEY_READ in conf and CONFIG_KEY_SEND in conf

if not config_has_state_keys(config):
    raise RuntimeError('StateBackend used outside a graph node')

Try / catch

try:
    files = backend._read_files()
except RuntimeError as e:
    if 'CONFIG_KEY_READ' in str(e):
        logger.error('StateBackend called outside a graph node; restructure the call')
    raise

Prevention

When it happens

Trigger: Calling StateBackend methods from a runnable/chain without LangGraph's Pregel runtime; invoking a tool that uses the backend outside a graph node; constructing a config dict manually without the CONFIG_KEY_READ/CONFIG_KEY_SEND entries.

Common situations: Migrating tools from plain LangChain to deep agents; testing with `config={'configurable': {}}` that looks plausible but lacks the internals; wrapping backend calls in background tasks that drop the injected config.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/f3a22df038f3db81. Report an issue: GitHub.