{"record":{"id":"211416244361f5ea","repo":"can1357/oh-my-pi","slug":"path-original-uses-internal-scheme-prefix","errorCode":null,"errorMessage":"Path \"${original}\" uses internal scheme \"${prefix}\" and must be resolved through the proper protocol handler, not as a filesystem path.","messagePattern":"Path \"(.+?)\" uses internal scheme \"(.+?)\" and must be resolved through the proper protocol handler, not as a filesystem path\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/path-utils.ts","lineNumber":490,"sourceCode":" * because `write` addresses a whole file, not a partial range, and silently\n * stripping it would write to a path the caller never named. Non-URL paths and\n * URLs without a selector pass through unchanged.\n */\nexport function peelWriteUrlSelector(rawPath: string): string {\n\tconst { path, sel } = splitInternalUrlSel(rawPath);\n\tif (sel === undefined) return rawPath;\n\t// Case-insensitive to match read's selector grammar (parseSel + the /i regexes above).\n\tif (/^(?:raw|conflicts)$/i.test(sel)) return path;\n\tthrow new ToolError(\n\t\t`write does not accept the trailing selector \":${sel}\" — it writes a whole file. ` +\n\t\t\t`Remove \":${sel}\", or if the filename truly ends with it, percent-encode the \":\" as %3A.`,\n\t);\n}\n\nfunction assertNotInternalUrl(expanded: string, original: string): void {\n\tfor (const prefix of TOP_LEVEL_INTERNAL_URL_PREFIXES) {\n\t\tif (expanded.startsWith(prefix)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Path \"${original}\" uses internal scheme \"${prefix}\" and must be resolved through the proper protocol handler, not as a filesystem path.`,\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport function normalizeLocalScheme(filePath: string): string {\n\treturn filePath.replace(/^(local:)\\/(?!\\/)/, \"$1//\");\n}\n\nexport function isInternalUrlPath(filePath: string): boolean {\n\tconst normalized = normalizeLocalScheme(filePath);\n\tconst expandedAndNormalized = normalizeLocalScheme(expandPath(normalized));\n\tfor (const prefix of TOP_LEVEL_INTERNAL_URL_PREFIXES) {\n\t\tif (expandedAndNormalized.startsWith(prefix)) return true;\n\t}\n\treturn false;\n}","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/path-utils.ts#L472-L508","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nresolveToCwd(\"session://abc123/transcript.jsonl\", cwd)\n// after — use the protocol-aware resolver\ninternalRouter.resolve(\"session://abc123/transcript.jsonl\", { cwd })","handlingStrategy":"validation","validationCode":"const INTERNAL_SCHEMES = [\"session://\", \"issue://\", \"local://\", \"history://\", \"agent://\", \"skill://\", \"pr://\"];\nif (INTERNAL_SCHEMES.some(s => p.startsWith(s))) {\n  throw new Error(`Route ${p} through the protocol-aware API, not the filesystem path API`);\n}","typeGuard":"const isInternalUrl = (p) => /^[a-z][a-z0-9+.-]*:\\/\\//i.test(p) && /^(session|issue|local|history|agent|skill|pr):\\/\\//i.test(p);","tryCatchPattern":"try { return resolveToCwd(p, cwd); } catch (e) {\n  if (String(e.message).includes('internal scheme')) return internalRouter.resolve(p, { cwd });\n  throw e;\n}","preventionTips":["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."],"tags":["path","internal-url","scheme","validation"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}