can1357/oh-my-pi · error · Error
Path "${original}" uses internal scheme "${prefix}" and must
Error message
Path "${original}" uses internal scheme "${prefix}" and must be resolved through the proper protocol handler, not as a filesystem path. What it means
resolveToCwd refuses to treat strings that begin with an internal scheme prefix (e.g. `session://`, `issue://`, `local://`) as ordinary filesystem paths. Internal URLs must go through their dedicated protocol handlers; bypassing them would resolve a bogus relative directory under the cwd.
Source
Thrown at packages/coding-agent/src/tools/path-utils.ts:490
* because `write` addresses a whole file, not a partial range, and silently
* stripping it would write to a path the caller never named. Non-URL paths and
* URLs without a selector pass through unchanged.
*/
export function peelWriteUrlSelector(rawPath: string): string {
const { path, sel } = splitInternalUrlSel(rawPath);
if (sel === undefined) return rawPath;
// Case-insensitive to match read's selector grammar (parseSel + the /i regexes above).
if (/^(?:raw|conflicts)$/i.test(sel)) return path;
throw new ToolError(
`write does not accept the trailing selector ":${sel}" — it writes a whole file. ` +
`Remove ":${sel}", or if the filename truly ends with it, percent-encode the ":" as %3A.`,
);
}
function assertNotInternalUrl(expanded: string, original: string): void {
for (const prefix of TOP_LEVEL_INTERNAL_URL_PREFIXES) {
if (expanded.startsWith(prefix)) {
throw new Error(
`Path "${original}" uses internal scheme "${prefix}" and must be resolved through the proper protocol handler, not as a filesystem path.`,
);
}
}
}
export function normalizeLocalScheme(filePath: string): string {
return filePath.replace(/^(local:)\/(?!\/)/, "$1//");
}
export function isInternalUrlPath(filePath: string): boolean {
const normalized = normalizeLocalScheme(filePath);
const expandedAndNormalized = normalizeLocalScheme(expandPath(normalized));
for (const prefix of TOP_LEVEL_INTERNAL_URL_PREFIXES) {
if (expandedAndNormalized.startsWith(prefix)) return true;
}
return false;
}View on GitHub (pinned to 9690622007)
Solutions
- Route the value through the tool/API that supports internal URLs (the one that uses InternalUrlRouter), not the filesystem path API.
- If you actually mean a local path, strip the internal scheme prefix and pass the real filesystem path.
- Check the origin of the value (config, session state) — it likely comes from an internal-URL context and is being fed to the wrong tool.
Example fix
// before
resolveToCwd("session://abc123/transcript.jsonl", cwd)
// after — use the protocol-aware resolver
internalRouter.resolve("session://abc123/transcript.jsonl", { cwd }) Defensive patterns
Strategy: validation
Validate before calling
const INTERNAL_SCHEMES = ["session://", "issue://", "local://", "history://", "agent://", "skill://", "pr://"];
if (INTERNAL_SCHEMES.some(s => p.startsWith(s))) {
throw new Error(`Route ${p} through the protocol-aware API, not the filesystem path API`);
} Type guard
const isInternalUrl = (p) => /^[a-z][a-z0-9+.-]*:\/\//i.test(p) && /^(session|issue|local|history|agent|skill|pr):\/\//i.test(p);
Try / catch
try { return resolveToCwd(p, cwd); } catch (e) {
if (String(e.message).includes('internal scheme')) return internalRouter.resolve(p, { cwd });
throw e;
} Prevention
- Keep internal-URL handling in one router module; never feed scheme strings to fs APIs.
- Sanitize persisted config values for stale scheme prefixes.
- Type-brand internal URLs distinctly from filesystem paths.
When it happens
Trigger: Passing a path starting with one of TOP_LEVEL_INTERNAL_URL_PREFIXES into a tool or helper that routes through resolveToCwd/assertNotInternalUrl instead of the internal-URL router — e.g. using `session://...` as a plain file path in grep/search/write.
Common situations: Agent or script mixes internal URL schemes with filesystem paths; a stored config value retains a scheme prefix after the protocol feature was removed or renamed; hand-building paths from parsed internal URLs.
Related errors
- Snippet file not found: ${resolved}
- ACP cwd must be absolute: ${cwd}
- Security knowledge base is not a file: ${input}
- Security output directory must be outside the scanned reposi
- Security output path exists and is not a directory
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/211416244361f5ea.
Report an issue: GitHub.