can1357/oh-my-pi · error · ToolError
Path traversal is not allowed in skill:// URLs
Error message
Path traversal is not allowed in skill:// URLs
What it means
Even with a well-formed relative path, the resolved absolute target must stay inside the skill's base directory. The function resolves both paths and rejects any result that escapes the base (the classic ".." symlink-or-path traversal defense), throwing this ToolError on escape.
Source
Thrown at packages/coding-agent/src/tools/bash-skill-urls.ts:105
let relativePath: string;
try {
relativePath = decodeURIComponent(rawPath.slice(1));
} catch {
throw new ToolError(`Invalid skill:// URL path encoding: ${url}`);
}
try {
validateRelativePath(relativePath);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new ToolError(message);
}
const targetPath = path.join(skill.baseDir, relativePath);
const resolvedPath = path.resolve(targetPath);
const resolvedBaseDir = path.resolve(skill.baseDir);
if (!resolvedPath.startsWith(resolvedBaseDir + path.sep) && resolvedPath !== resolvedBaseDir) {
throw new ToolError("Path traversal is not allowed in skill:// URLs");
}
// Agent Plugin skills (§4.1): the resource must canonically resolve within
// the plugin root. Fail closed: a dangling or unresolvable path is rejected
// rather than handed to bash, where writing through it could create the
// outside target. Symlinks may target other files inside the same package.
if (skill.containRoot) {
const contained = resolveContainedPathSync(skill.containRoot, resolvedPath);
if (contained.status === "outside") {
throw new ToolError(`skill:// path resolves outside the plugin root: ${url}`);
}
if (contained.status === "missing") {
throw new ToolError(`skill:// path does not exist: ${url}`);
}
return contained.realPath;
}
return resolvedPath;
}View on GitHub (pinned to 9690622007)
Solutions
- Remove ".." traversal from the URL path; target only files inside the skill directory.
- If you need a file outside the skill, use the appropriate tool (read tool / plain path) with the proper permissions.
- Check that the skill's baseDir is where you expect; the URL is interpreted relative to it.
Example fix
// before
resolveSkillUrlToPath("skill://my-skill/../../secrets.env", skills);
// after
resolveSkillUrlToPath("skill://my-skill/config.env", skills); Defensive patterns
Strategy: validation
Validate before calling
import * as path from "node:path";
const rel = decodeURIComponent(new URL(url).pathname.slice(1));
const resolved = path.resolve(skill.baseDir, rel);
if (!resolved.startsWith(path.resolve(skill.baseDir) + path.sep)) {
throw new Error(`refusing: ${url} escapes skill directory`);
} Try / catch
try {
return resolveSkillUrlToPath(url, skills);
} catch (e) {
if (e instanceof ToolError && e.message.includes("Path traversal")) {
// do not retry; treat as a security rejection and report to the caller
} else throw e;
} Prevention
- Never construct skill URLs containing ".." segments, even for 'convenience' access.
- Sanitize any user/model-supplied path with a allowlist of safe segments.
- Treat traversal attempts as a security signal, not a recoverable input error.
When it happens
Trigger: Any skill:// URL whose final resolved path (after path.join/resolve) lands outside skill.baseDir — typically "skill://name/../../outside.txt" or a baseDir-relative escape via symlinks not covered by containRoot.
Common situations: Prompt-injection or a wandering agent tries to read files outside a skill package; a legitimately relocated skill directory makes previously valid relative paths resolve oddly; hardcoded traversal in generated commands.
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
- Destination paths cannot contain parent traversal or NUL byt
- Shared-folder destination escapes its configured root
- Refusing to download outside the workspace: ${downloadPath}
- Absolute paths are not allowed in ${scheme}:// URLs: ${rawPa
- Path traversal (..) is not allowed in ${scheme}:// URLs: ${r
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5cc2e847209c7703.
Report an issue: GitHub.