{"record":{"id":"f3a22df038f3db81","repo":"langchain-ai/deepagents","slug":"statebackend-requires-config-key-read-config-key","errorCode":null,"errorMessage":"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\": {...}})","messagePattern":"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\\((.+?)\\}\\)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/backends/state.py","lineNumber":78,"sourceCode":"            config = get_config()\n        except RuntimeError:\n            msg = (\n                \"StateBackend must be used inside a LangGraph graph execution \"\n                \"(e.g. via create_deep_agent). It cannot read or write state \"\n                \"outside of a graph context. To pre-populate files, pass them \"\n                'on invoke: agent.invoke({\"messages\": [...], \"files\": {...}})'\n            )\n            raise RuntimeError(msg) from None\n        configurable = config.get(\"configurable\", {})\n        if CONFIG_KEY_READ not in configurable:\n            msg = (\n                \"StateBackend requires CONFIG_KEY_READ / CONFIG_KEY_SEND in \"\n                \"the LangGraph config. Make sure the backend is used inside \"\n                \"a graph node or tool, not called directly. To pre-populate \"\n                \"files, pass them on invoke: \"\n                'agent.invoke({\"messages\": [...], \"files\": {...}})'\n            )\n            raise RuntimeError(msg)\n        return config\n\n    def _read_files(self) -> dict[str, Any]:\n        \"\"\"Read the current `files` channel via Pregel internals.\n\n        Uses `CONFIG_KEY_READ` to read state directly — this lets us\n        initialize StateBackend once and fetch state on demand from any\n        graph context (tools, middleware nodes, etc.).\n\n        `fresh=True` applies any pending task writes through the channel's\n        reducer before returning, giving read-your-writes semantics within\n        a single superstep — e.g. a tool that writes a file and then reads\n        it back, or a code interpreter that issues multiple sub-tool calls\n        inside one eval.\n        \"\"\"\n        config = self._get_config()\n        read = config[\"configurable\"][CONFIG_KEY_READ]\n        fresh = True","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/backends/state.py#L60-L96","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run the backend within a LangGraph graph node or tool (via `create_deep_agent`) so LangGraph injects CONFIG_KEY_READ/CONFIG_KEY_SEND","In unit tests, use the repo's state-backend test fixtures that build a fake config containing both keys","Pass files on invoke (`agent.invoke({'messages': [...], 'files': {...}})`) instead of touching the backend directly","Use a non-state backend if you need standalone file I/O"],"exampleFix":"// before\nbackend = StateBackend()\nbackend.read(['/a.txt'], config={'configurable': {}})  # RuntimeError\n\n// after\nagent = create_deep_agent(backend=StateBackend(), tools=[...])\nresult = agent.invoke({'messages': [{'role': 'user', 'content': 'read /a.txt'}]})","handlingStrategy":"validation","validationCode":"def config_has_state_keys(config) -> bool:\n    conf = (config or {}).get('configurable', {})\n    return CONFIG_KEY_READ in conf and CONFIG_KEY_SEND in conf\n\nif not config_has_state_keys(config):\n    raise RuntimeError('StateBackend used outside a graph node')","typeGuard":null,"tryCatchPattern":"try:\n    files = backend._read_files()\nexcept RuntimeError as e:\n    if 'CONFIG_KEY_READ' in str(e):\n        logger.error('StateBackend called outside a graph node; restructure the call')\n    raise","preventionTips":["Invoke the backend only from within LangGraph nodes/tools created via create_deep_agent","In tests, use the repo's fake-config fixtures that inject CONFIG_KEY_READ/CONFIG_KEY_SEND","Don't hand-construct configs for StateBackend; let LangGraph supply them","Never assume a RunnableConfig implies graph context — check the keys"],"tags":["python","langgraph","state-backend","config","runtime-error"],"backgroundTag":"missing-config-key","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}