shareAI-lab/learn-claude-code · error · ValueError

Path escapes workspace: {p}

Error message

Path escapes workspace: {p}

What it means

Identical guard at agents/s10_team_protocols.py:300 — this stage adds structured team protocols on top of s09 and reuses _safe_path for the base file tools. Any file tool path that resolves outside WORKDIR (Path.cwd() at launch) is rejected.

Source

Thrown at agents/s10_team_protocols.py:300

        if not self.config["members"]:
            return "No teammates."
        lines = [f"Team: {self.config['team_name']}"]
        for m in self.config["members"]:
            lines.append(f"  {m['name']} ({m['role']}): {m['status']}")
        return "\n".join(lines)

    def member_names(self) -> list:
        return [m["name"] for m in self.config["members"]]


TEAM = TeammateManager(TEAM_DIR)


# -- Base tool implementations (these base tools are unchanged from s02) --
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)"

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Make handoff/protocol messages use workspace-relative paths
  2. Convert absolute paths to relative before file tool calls
  3. Start the agent with cwd = workspace root

Example fix

# before
_safe_path("/home/beagle/proj/notes.md")  # ValueError: Path escapes workspace

# after
_safe_path("notes.md")
Defensive patterns

Strategy: validation

Validate before calling

import os
from pathlib import Path

def relay_safe_path(p: str, workdir: Path) -> str | None:
    try:
        cand = Path(p)
        abs_c = (cand if cand.is_absolute() else workdir / cand).resolve()
        return os.path.relpath(abs_c, workdir) if abs_c.is_relative_to(workdir) else None
    except (OSError, ValueError, RuntimeError):
        return None

rel = relay_safe_path(handoff_path, WORKDIR)
assert rel, "handoff path escapes workspace; request a relative path in the protocol"

Type guard

def is_protocol_safe_path(p: object, workdir) -> bool:
    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:
    return f"Protocol path {p} rejected. Handoffs must reference workspace-relative paths."

Prevention

When it happens

Trigger: File tool calls carrying absolute paths or traversals, typically paths copied from protocol messages, handoff notes, or teammate configs. Symlink escape and wrong launch cwd are the other two routes.

Common situations: Protocol handoffs that reference files by absolute path; models forwarding those verbatim. Harness launched from a parent directory so WORKDIR is too narrow.

Related errors


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