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 the s12 agent tool layer when a user-supplied relative path resolves outside WORKDIR. The function joins the path onto WORKDIR, resolves symlinks and .. segments, then requires the result to be inside WORKDIR — a classic sandbox containment check that blocks path traversal. All file tools (read/write/edit) route through it, so any traversal attempt fails before touching the filesystem.
Source
Thrown at agents/s12_worktree_task_isolation.py:481
"worktree.keep",
task={"id": wt.get("task_id")} if wt.get("task_id") is not None else {},
worktree={
"name": name,
"path": wt.get("path"),
"status": "kept",
},
)
return json.dumps(kept, indent=2) if kept else f"Error: Unknown worktree '{name}'"
WORKTREES = WorktreeManager(REPO_ROOT, TASKS, EVENTS)
# -- Base tools (kept minimal, same style as previous sessions) --
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,
timeout=120,
)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"View on GitHub (pinned to 985456f4ad)
Solutions
- Strip any leading '/' and re-express the path relative to WORKDIR before calling the tool
- Convert absolute paths under WORKDIR to relative: path.relative_to(WORKDIR) when possible
- If a legitimate symlink inside WORKDIR points outside, restructure so the data lives inside the workspace
- If WORKDIR is itself a symlink, pass the resolved real path when constructing the agent
Example fix
// before
run_read("/tmp/errlookup-AdzUmp/notes.txt") # absolute -> escapes
// after
from pathlib import Path
rel = Path("/tmp/errlookup-AdzUmp/notes.txt").relative_to(WORKDIR)
run_read(str(rel)) Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def in_workspace(p: str) -> bool:
try:
(WORKDIR / p).resolve().relative_to(WORKDIR.resolve())
return True
except ValueError:
return False
# or normalize absolute paths first:
def to_relative(p: str) -> str:
ap = Path(p).resolve()
return str(ap.relative_to(WORKDIR.resolve())) Try / catch
try:
safe_path(p)
except ValueError:
p = str(Path(p).resolve().relative_to(WORKDIR.resolve())) # only if truly inside
safe_path(p) Prevention
- Always pass workspace-relative paths to file tools
- Convert absolute paths with relative_to(WORKDIR) before calling
- Audit symlinks inside the workspace periodically
- Ensure WORKDIR is the real (resolved) path at agent startup
When it happens
Trigger: Passing "../../etc/passwd", an absolute path like "/etc/hosts" (WORKDIR / '/etc/hosts' yields /etc/hosts, outside WORKDIR), or a path that traverses a symlink pointing outside WORKDIR. Also fires on benign-looking inputs when WORKDIR itself is a symlink that resolves elsewhere.
Common situations: Agents emitting absolute paths from tool output (e.g. copying a path from an error message or git output); symlinks inside the workspace pointing to system directories; WORKDIR configured as a symlinked path so resolve() moves the root out from under the containment check.
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/422534c8936ad761.
Report an issue: GitHub.