shareAI-lab/learn-claude-code · error · ValueError
Path escapes workspace: {p}
Error message
Path escapes workspace: {p} What it means
Same safe_path() containment guard as in the other session modules, duplicated into the consolidated agent s_full.py. It resolves WORKDIR-joined paths and rejects anything landing outside WORKDIR, protecting the file tools from path traversal. Because it appears in the merged module, every file tool in the full agent (read, write, edit, etc.) inherits the same restriction.
Source
Thrown at agents/s_full.py:77
TEAM_DIR = WORKDIR / ".team"
INBOX_DIR = TEAM_DIR / "inbox"
TASKS_DIR = WORKDIR / ".tasks"
SKILLS_DIR = WORKDIR / "skills"
TRANSCRIPT_DIR = WORKDIR / ".transcripts"
TOKEN_THRESHOLD = 100000
POLL_INTERVAL = 5
IDLE_TIMEOUT = 60
VALID_MSG_TYPES = {"message", "broadcast", "shutdown_request",
"shutdown_response", "plan_approval_response"}
# === SECTION: base_tools ===
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)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
def run_read(path: str, limit: int = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
if limit and limit < len(lines):View on GitHub (pinned to 985456f4ad)
Solutions
- Always pass paths relative to WORKDIR and without leading '..' segments
- Normalize absolute in-workspace paths with Path(p).relative_to(WORKDIR) before the call
- Audit symlinks inside the workspace with find WORKDIR -type l and remove ones pointing outside
- Verify the WORKDIR value used at agent construction matches where files actually live
Example fix
// before
run_edit("/home/user/proj/src/app.py", ...)
// after
run_edit("src/app.py", ...) Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def workspace_relative(p: str) -> str | None:
ap = Path(p)
if not ap.is_absolute():
ap = (WORKDIR / p)
try:
return str(ap.resolve().relative_to(WORKDIR.resolve()))
except ValueError:
return None # escapes workspace — do not call the tool
if (rel := workspace_relative(p)) is not None:
run_read(rel) Prevention
- Strip absolute prefixes from agent-echoed paths before reuse
- Keep all working files inside WORKDIR
- Treat this error as a security signal — log it, do not silently rewrite the path
When it happens
Trigger: A tool call with "../secrets.env", an absolute path from a prior error message, or a workspace symlink chain escaping WORKDIR after resolve(). Any file tool in s_full.py (run_read/run_write/run_edit) receives the path.
Common situations: Agent copies an absolute path out of a traceback or git output and feeds it back to a file tool; nested symlinked node_modules-style layouts resolving outside the tree; WORKDIR misconfigured at agent startup.
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/87713dcf06ad2c00.
Report an issue: GitHub.