yamadashy/repomix · error · PathScopeError

Path "${input}" resolves outside workspace root.

Error message

Path "${input}" resolves outside workspace root.

What it means

After lexical checks pass, resolveWithinRoot resolves the candidate with path.resolve and verifies it remains inside the workspace root via isInside. If a relative-but-tricky input (symlinks in the middle, weird separators, overlapping names like 'root-evil') resolves outside root, this PathScopeError is thrown.

Source

Thrown at src/mcp/pathScope.ts:73

): Promise<string> => {
  const rootResolved = path.resolve(root);

  if (input === '' || input === '.') {
    return rootResolved;
  }

  // Reject anything that escapes the workspace root — absolute/drive/UNC, a "~"
  // home ref, or any ".." segment. A redundant leading "./" or ".\" is allowed.
  if (isEscapingPath(input)) {
    throw new PathScopeError(
      `Path "${input}" must be relative to workspace root — no "/", "~/", "../", drive, or ".." segment. Use e.g. "src/index.ts" or "." for whole workspace.`,
    );
  }

  const candidate = path.resolve(rootResolved, input);

  if (!isInside(rootResolved, candidate)) {
    throw new PathScopeError(`Path "${input}" resolves outside workspace root.`);
  }

  // Resolve symlinks to catch a link inside root that points back out. If a path
  // can't be resolved via realpath (e.g. the target does not exist yet, or the
  // root itself can't be stat'd), fall back to the lexical path — it is already
  // confined lexically above.
  let realRoot: string;
  try {
    realRoot = (await deps.realpath(rootResolved)) as string;
  } catch {
    realRoot = rootResolved;
  }
  let realCandidate: string;
  try {
    realCandidate = (await deps.realpath(candidate)) as string;
  } catch {
    return candidate;
  }

View on GitHub (pinned to f465ad9093)

Solutions

  1. Use a plain relative path under the workspace root
  2. Remove or relocate symlinks inside the workspace that point outside it
  3. Verify with path.resolve yourself which absolute path results and adjust the input
  4. If this blocks a legitimate layout, restructure so the target lives inside the root

Example fix

// before (intermediate symlink out of root)
resolveWithinRoot(root, 'vendor/link/../secret.txt')
// after
resolveWithinRoot(root, 'src/actual-file.ts')
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path';
const candidate = path.resolve(root, input);
if (candidate !== root && !candidate.startsWith(root + path.sep)) {
  throw new Error(`path escapes workspace root: ${input}`);
}

Try / catch

try {
  const abs = await resolveWithinRoot(root, input);
} catch (err) {
  if (err instanceof PathScopeError && err.message.includes('resolves outside')) {
    console.error(`${input} normalizes outside the root; inspect intermediate symlinks.`);
  } else throw err;
}

Prevention

When it happens

Trigger: Input passes isEscapingPath but path.resolve(root, input) lands outside root — e.g. after normalization of unusual segments — or an intermediate symlink points out of the root.

Common situations: Agents submitting crafted relative paths; directories whose names share a prefix with root; intermediate symlinked dirs inside the workspace pointing elsewhere on disk.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of yamadashy/repomix@f465ad9093 (2026-08-29). Data as JSON: /api/errors/74b76e8d5e8eda11. Report an issue: GitHub.