can1357/oh-my-pi · error · ToolError
Path traversal (..) is not allowed in ${scheme}:// URLs: ${r
Error message
Path traversal (..) is not allowed in ${scheme}:// URLs: ${rawPath} What it means
`resolveUnderRoot` normalizes the decoded relative path and rejects any result containing `..` segments (leading `..`, `/../`, or trailing `/..`). This blocks parent-directory traversal inside `scheme://` URLs, keeping sandbox file access confined to the mounted root — a deliberate security guard, not a bug.
Source
Thrown at packages/coding-agent/src/eval/js/shared/helpers.ts:137
}
/** Resolve an internal-URL relative path under its root, mirroring the host
* local-protocol handler: decode, reject absolute/traversal, confine to root. */
function resolveUnderRoot(scheme: string, root: string, rawRelative: string, rawPath: string): string {
let relative: string;
try {
relative = decodeURIComponent(rawRelative.replaceAll("\\", "/"));
} catch {
throw new ToolError(`Invalid URL encoding in ${scheme}:// path: ${rawPath}`);
}
const rootPath = path.resolve(root);
if (relative === "") return rootPath;
if (path.isAbsolute(relative)) {
throw new ToolError(`Absolute paths are not allowed in ${scheme}:// URLs: ${rawPath}`);
}
const normalized = path.normalize(relative);
if (normalized.startsWith("..") || normalized.includes("/../") || normalized.includes("/..")) {
throw new ToolError(`Path traversal (..) is not allowed in ${scheme}:// URLs: ${rawPath}`);
}
const resolved = path.resolve(rootPath, normalized);
if (resolved !== rootPath && !resolved.startsWith(`${rootPath}${path.sep}`)) {
throw new ToolError(`${scheme}:// path escapes its root: ${rawPath}`);
}
return resolved;
}
async function resolveRegularFile(
ctx: HelperContext,
rawPath: string,
): Promise<{ filePath: string; file: Bun.BunFile; size: number }> {
const filePath = resolveHelperPath(ctx, rawPath, "read");
const file = Bun.file(filePath);
const stat = await file.stat();
if (stat.isDirectory()) {
throw new ToolError(`Directory paths are not supported by read(): ${filePath}`);
}View on GitHub (pinned to 9690622007)
Solutions
- Reference files within the mounted root using paths with no `..` segments.
- Ask the host to mount the directory you actually need as an additional localRoot instead of traversing up.
- Use absolute plain paths only if the environment permits them and drop the scheme:// form.
Example fix
// before
const s = await read("local://../project/config.json");
// after
const s = await read("/abs/path/project/config.json"); // or mount project as a root and use local://project/config.json Defensive patterns
Strategy: validation
Validate before calling
const rel = decodeURIComponent(url.replace(/^\w+:\/\//, "").replaceAll("\\", "/"));
if (rel.split("/").includes("..")) throw new Error("traversal not allowed"); Try / catch
try {
return await read(url);
} catch (err) {
if (String(err?.message).includes("Path traversal")) {
// do not sanitize-and-retry silently: log and surface to the user
throw new Error(`blocked traversal attempt: ${url}`);
}
throw err;
} Prevention
- Resolve and normalize relative paths against the intended root before building scheme URLs.
- Reject or canonicalize any path segment equal to `..` in generated code.
- Treat repeated traversal attempts from generated code as a signal to mount the correct root instead.
When it happens
Trigger: Calling read()/write() with traversal paths such as `local://../secrets.txt`, `local://a/../../etc/passwd`, or `local://dir/..%2f..%2fx` (decodes to `..` segments).
Common situations: LLM-generated eval code attempting to reach files outside the artifacts root; constructing paths by concatenating untrusted input; attempting to walk up from the session's mounted directory.
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
- Absolute paths are not allowed in ${scheme}:// URLs: ${rawPa
- Destination paths cannot contain parent traversal or NUL byt
- Shared-folder destination escapes its configured root
- ${scheme}:// path escapes its root: ${rawPath}
- Unsafe #{scheme}:// path (absolute or traversal): #{path}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/50b84f11743d443f.
Report an issue: GitHub.