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

  1. Direct background jobs to write their logs inside the workspace (e.g. bg_*.log at the root) so later reads are relative
  2. Pass workspace-relative paths to file tools
  3. 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

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


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/5c4b11335273de9f. Report an issue: GitHub.