shareAI-lab/learn-claude-code · error · ValueError
Path escapes workspace: {p}
Error message
Path escapes workspace: {p} What it means
The workspace sandbox guard at agents/s08_background_tasks.py:118 in the background-tasks agent. It applies to the synchronous file tools of this script; BackgroundManager processes run shell commands in the background and are not routed through safe_path, so this error always comes from a direct file tool call.
Source
Thrown at agents/s08_background_tasks.py:118
lines.append(f"{tid}: [{t['status']}] {t['command'][:60]}")
return "\n".join(lines) if lines else "No background tasks."
def drain_notifications(self) -> list:
"""Return and clear all pending completion notifications."""
with self._lock:
notifs = list(self._notification_queue)
self._notification_queue.clear()
return notifs
BG = BackgroundManager()
# -- Tool implementations --
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
- Direct background jobs to write their logs inside the workspace (e.g. bg_*.log at the root) so later reads are relative
- Pass workspace-relative paths to file tools
- Use the background manager's own output/notification APIs rather than raw file reads for job output
Example fix
# before
run_bg("pytest > /tmp/job.log", name="tests")
read_file("/tmp/job.log") # ValueError: Path escapes workspace
# after
run_bg("pytest > job_tests.log", name="tests")
read_file("job_tests.log") Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def is_bg_log_path(p: str, workdir: Path) -> bool:
try:
return (workdir / p).resolve().is_relative_to(workdir)
except (OSError, RuntimeError):
return False
# at job launch, force logs into the workspace:
command = command_with_redirects_into("bg_logs/") # e.g. "pytest > bg_logs/tests.log 2>&1"
assert is_bg_log_path("bg_logs/tests.log", WORKDIR) Type guard
def is_workspace_log_path(p: object) -> bool:
return isinstance(p, str) and bool(p) and not p.startswith(("/", "~")) and ".." not in Path(p).parts Try / catch
try:
path = safe_path(p)
except ValueError:
return f"{p} is outside the workspace. Redirect background output into the workspace (e.g. bg_<name>.log) and read it relatively." Prevention
- Redirect background job output to files inside the workspace at launch time
- Use the background manager's notification/output APIs instead of raw reads
- Never pass /tmp-style absolute log paths to the sandboxed file tools
When it happens
Trigger: A file tool call with an absolute or `../` path, or a path through an outward symlink. Common variant: the model inspects background job output files by their absolute path (e.g. under /tmp) instead of a workspace-relative log path.
Common situations: Background jobs writing logs outside the workspace, then the model trying to read those logs with the sandboxed file tool. cwd mismatch at launch.
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/5c4b11335273de9f.
Report an issue: GitHub.