can1357/oh-my-pi · error
Absolute paths are not allowed in skill:// URLs
Error message
Absolute paths are not allowed in skill:// URLs
What it means
validateRelativePath() guards skill:// URL path components against unsafe paths. Any path that Node's path.isAbsolute() accepts (e.g. '/etc/passwd', 'C:\\data') is rejected because skill:// paths must stay relative to the skill's base directory. This is a security boundary preventing absolute-path escapes when the path is later joined with skill.baseDir.
Source
Thrown at packages/coding-agent/src/internal-urls/skill-protocol.ts:30
import * as path from "node:path";
import { isEnoent } from "@oh-my-pi/pi-utils";
import { resolveContainedPath } from "../discovery/contained-path";
import { getActiveSkills } from "../extensibility/skills";
import { isMarkdownPath } from "../utils/lang-from-path";
import { buildDirectoryResource } from "./filesystem-resource";
import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext, UrlCompletion } from "./types";
function getContentType(filePath: string): InternalResource["contentType"] {
if (isMarkdownPath(filePath)) return "text/markdown";
return "text/plain";
}
/**
* Validate that a path is safe (no traversal, no absolute paths).
*/
export function validateRelativePath(relativePath: string): void {
if (path.isAbsolute(relativePath)) {
throw new Error("Absolute paths are not allowed in skill:// URLs");
}
const normalized = path.normalize(relativePath);
if (
relativePath.split(/[\\/]/).includes("..") ||
normalized.startsWith("..") ||
normalized.includes("/../") ||
normalized.includes("/..")
) {
throw new Error("Path traversal (..) is not allowed in skill:// URLs");
}
}
/**
* Handler for skill:// URLs.
*/
export class SkillProtocolHandler implements ProtocolHandler {
readonly scheme = "skill";View on GitHub (pinned to 9690622007)
Solutions
- Pass a path relative to the skill's base directory, e.g. skill://my-skill/examples/foo.md instead of skill://my-skill//abs/path
- Strip the skill.baseDir prefix from your absolute path (path.relative) before embedding it in the URL
- If the file lives outside the skill directory, access it through file:// rather than skill://
Example fix
// before
resolve(`skill://my-skill${path.resolve('/home/me/skills/my-skill/notes.md')}`)
// after
const rel = path.relative(skill.baseDir, '/home/me/skills/my-skill/notes.md');
resolve(`skill://my-skill/${rel}`) Defensive patterns
Strategy: validation
Validate before calling
import * as path from 'node:path';
export function toSkillUrl(skillName: string, baseDir: string, absolutePath: string): string {
const rel = path.relative(baseDir, absolutePath);
if (path.isAbsolute(rel) || rel.startsWith('..')) {
throw new Error(`${absolutePath} is not inside skill ${skillName}`);
}
return `skill://${skillName}/${rel}`;
} Type guard
function isSafeRelative(p: string): boolean {
return !path.isAbsolute(p) && !p.split(/[\\/]/).includes('..');
} Try / catch
try {
return await handler.resolve(url, ctx);
} catch (err) {
if (err instanceof Error && err.message.includes('Absolute paths are not allowed')) {
// convert to a relative path or fall back to file://
}
throw err;
} Prevention
- Never embed filesystem absolute paths in skill:// URLs
- Always derive the path with path.relative(skillBaseDir, target)
- Use file:// for files outside the skill directory
When it happens
Trigger: resolve() on skill://<name>/... where the pathname (after decodeURIComponent) starts with '/', 'X:', or '\\\\server\\share'; also direct calls to validateRelativePath(), extractRelativePath(), splitMemoryGlobPattern(), resolveMemoryUrlToPath(), decodeVaultPath(), or validateQueryPath() with an absolute path.
Common situations: Concatenating a filesystem absolute path into a skill:// URL instead of a relative one; URL-encoding an absolute path that decodes to a leading slash; building URLs from user-supplied file paths without stripping the skill directory prefix; Windows drive-letter paths leaking into URLs.
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
- Path traversal (..) is not allowed in skill:// URLs
- vault:// URL escapes vault root
- Provider delete URL must not embed an account credential
- ${destination} returned an unsupported upload URL
- Destination paths cannot contain parent traversal or NUL byt
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4d535b2facdc5372.
Report an issue: GitHub.