shareAI-lab/learn-claude-code · error · ValueError
Path escapes workspace: {p}
Error message
Path escapes workspace: {p} What it means
Raised by safe_path() in agents/s02_tool_use.py:44, the workspace sandbox guard for every file tool in this agent script. It resolves the model-supplied path against WORKDIR (the process cwd at launch) with Path.resolve(), then rejects it unless the result is still inside WORKDIR. In pathlib, `WORKDIR / p` with an absolute `p` yields that absolute path, so any absolute path outside the workspace, or any `../` traversal that resolves outside it, trips this check.
Source
Thrown at agents/s02_tool_use.py:44
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv(override=True)
if os.getenv("ANTHROPIC_BASE_URL"):
os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)
WORKDIR = Path.cwd()
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
MODEL = os.environ["MODEL_ID"]
SYSTEM = f"You are a coding agent at {WORKDIR}. Use tools to solve tasks. Act, don't explain."
def safe_path(p: str) -> Path:
path = (WORKDIR / p).resolve()
if not path.is_relative_to(WORKDIR):
raise ValueError(f"Path escapes workspace: {p}")
return path
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
capture_output=True, text=True,
encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
def run_read(path: str, limit: int = None) -> str:View on GitHub (pinned to 985456f4ad)
Solutions
- Call file tools with paths relative to the workspace root, e.g. "src/main.py" not "/home/user/proj/src/main.py"
- If the path is absolute but inside the workspace, strip the WORKDIR prefix before passing it (os.path.relpath(p, WORKDIR))
- Make the system prompt tell the model explicitly that tool paths must be relative to the stated workspace directory
- Launch the agent with cwd set to the actual workspace so WORKDIR matches where files live
- If symlinks inside the workspace legitimately point outside, replace the sandbox with an allowlist of permitted roots instead of widening the check blindly
Example fix
// before (tool call)
{"op": "read", "path": "/home/user/proj/src/main.py"}
// raises ValueError: Path escapes workspace
// after (tool call)
{"op": "read", "path": "src/main.py"}
// after (harness-side normalization before calling safe_path)
import os
rel = os.path.relpath(os.path.abspath(p), WORKDIR)
path = safe_path(rel) Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def is_in_workspace(p: str, workdir: Path) -> bool:
try:
return (workdir / p).resolve().is_relative_to(workdir)
except (OSError, RuntimeError):
return False
# before any file tool call:
assert is_in_workspace(user_path, WORKDIR), f"{user_path} escapes workspace" Type guard
from pathlib import Path
def is_workspace_relative_path(p: str, workdir: Path) -> bool:
"""True iff p joins+resolves inside workdir (no escape via .., abs, or symlink)."""
if not isinstance(p, str) or not p:
return False
try:
return (workdir / p).resolve().is_relative_to(workdir)
except (OSError, RuntimeError):
return False Try / catch
try:
path = safe_path(p)
except ValueError as e:
# feed the rejection back to the model as a tool error with a hint
return f"Tool error: {e}. Pass a path relative to {WORKDIR} (no leading /, no ..)." Prevention
- State in the system prompt that all tool paths must be relative to WORKDIR
- Normalize model paths at the tool boundary: os.path.relpath against WORKDIR when the absolute path is already known to be inside it
- Launch the harness with cwd set to the workspace so WORKDIR is the intended root
- Never widen safe_path for absolute paths; convert the input instead
When it happens
Trigger: The LLM calls read_file/write_file/edit_file with an absolute path like "/etc/passwd", a path with leading slashes, a "../../secrets.env" traversal, or a symlink inside the workspace whose target lives outside (resolve() follows symlinks). It can also fire if the agent process was launched from a different directory than the intended workspace, making WORKDIR = Path.cwd() point somewhere the model's files are not under.
Common situations: Models habitually emit absolute paths copied from tool output or the system prompt (the prompt embeds the absolute WORKDIR). Broken setups where the harness is started from $HOME or / while the task files live in a project dir. Workspaces reached through symlinked paths (macOS /tmp -> /private/tmp style) can also resolve outside the literal WORKDIR.
Related errors
- Path escapes workspace: {p}
- Path escapes workspace: {p}
- Path escapes workspace: {p}
- Path escapes workspace: {p}
- Path escapes workspace: {p}
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/a754ae80ac2888da.
Report an issue: GitHub.