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

Path escapes workspace: {path}

Error message

Path escapes workspace: {path}

What it means

safe_path() resolves a user/model-supplied relative path against a base (the given cwd or WORKDIR) and requires the result to stay inside that base. Any absolute path outside the workspace, or a relative path using ../ that climbs out, is rejected before any file operation. It is the central confinement guard for read/write/edit/bash tools.

Source

Thrown at s15_integrated_harness/code.py:804

                    "\nUse load_skill(name) when a skill is relevant.")
    if context.get("memory_catalog"):
        sections.append(f"Memory catalog:\n{context['memory_catalog']}")
    if context.get("memories"):
        sections.append(f"Relevant memory records:\n{context['memories']}")
    mcp_names = list(mcp_clients.keys())
    if mcp_names:
        sections.append(f"Connected MCP servers: {', '.join(mcp_names)}")
    return "\n\n".join(sections)


# -- Basic Tools --


def safe_path(path: str, cwd: Path | None = None) -> Path:
    base = (cwd or WORKDIR).resolve()
    resolved = (base / path).resolve()
    if not resolved.is_relative_to(base):
        raise ValueError(f"Path escapes workspace: {path}")
    return resolved


_shell_processes: set[subprocess.Popen] = set()
_shell_process_lock = threading.RLock()


def _stop_process_group(process: subprocess.Popen):
    """Stop processes that remain in the command's original process group."""
    for sig in (signal.SIGTERM, signal.SIGKILL):
        try:
            os.killpg(process.pid, sig)
        except ProcessLookupError:
            return
        except OSError:
            return
        time.sleep(0.05)

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Use paths relative to the workspace root (or the tool's cwd) and stay inside it.
  2. If a file legitimately lives outside, copy it into the workspace first with shell access permitted by policy.
  3. Expand and pre-check paths caller-side: resolve against base and confirm is_relative_to before calling file tools.

Example fix

// before
read_file("../../etc/hosts")

// after
read_file("notes/etc-hosts-copy.txt")  // work inside WORKDIR
Defensive patterns

Strategy: type-guard

Validate before calling

from pathlib import Path

def inside_workspace(path: str, base: Path) -> bool:
    try:
        return (base / path).resolve().is_relative_to(base.resolve())
    except (TypeError, ValueError):
        return False

Type guard

def is_safe_relative(path: str) -> bool:
    return isinstance(path, str) and not path.startswith(("/", "~")) and ".." not in Path(path).parts

Try / catch

try:
    resolved = safe_path(path, cwd)
except ValueError as e:
    if "escapes workspace" in str(e):
        return f"Error: {e}. Use a path inside the workspace."
    raise

Prevention

When it happens

Trigger: Passing '/etc/passwd' or '~/secrets' (unexpanded) to a file tool; 'build/../../outside.txt'; a cwd argument that is itself outside WORKDIR combined with a relative path escaping it. Note ~ is NOT expanded, so '~/x' resolves under base as a literal '~' directory — paths outside base raise here.

Common situations: Model writing absolute paths from error messages; agents following a symlinked include to a system file; scripts assuming home-directory access inside a sandboxed harness.

Related errors


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