{"record":{"id":"5e7a3f234b81fee4","repo":"can1357/oh-my-pi","slug":"protocol-paths-are-not-supported-by-op-raw","errorCode":null,"errorMessage":"Protocol paths are not supported by ${op}(): ${rawPath}","messagePattern":"Protocol paths are not supported by (.+?)\\(\\): (.+?)","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/js/shared/helpers.ts","lineNumber":116,"sourceCode":"\nfunction resolvePath(ctx: HelperContext, value: string): string {\n\tif (path.isAbsolute(value)) return path.normalize(value);\n\treturn path.resolve(ctx.cwd(), value);\n}\n\n/**\n * Map a raw helper path to an absolute filesystem path. Plain paths resolve\n * against the cwd; an internal-URL whose scheme has an injected root (e.g.\n * `local://`) is rewritten under that root; any other `scheme://` is rejected\n * so we never silently create a literal `scheme:/` directory.\n */\nfunction resolveHelperPath(ctx: HelperContext, rawPath: string, op: \"read\" | \"write\"): string {\n\tconst match = INTERNAL_URL_RE.exec(rawPath);\n\tif (!match) return resolvePath(ctx, rawPath);\n\tconst scheme = match[1].toLowerCase();\n\tconst root = ctx.localRoots()[scheme];\n\tif (!root) {\n\t\tthrow new ToolError(`Protocol paths are not supported by ${op}(): ${rawPath}`);\n\t}\n\treturn resolveUnderRoot(scheme, root, match[2], rawPath);\n}\n\n/** Resolve an internal-URL relative path under its root, mirroring the host\n *  local-protocol handler: decode, reject absolute/traversal, confine to root. */\nfunction resolveUnderRoot(scheme: string, root: string, rawRelative: string, rawPath: string): string {\n\tlet relative: string;\n\ttry {\n\t\trelative = decodeURIComponent(rawRelative.replaceAll(\"\\\\\", \"/\"));\n\t} catch {\n\t\tthrow new ToolError(`Invalid URL encoding in ${scheme}:// path: ${rawPath}`);\n\t}\n\tconst rootPath = path.resolve(root);\n\tif (relative === \"\") return rootPath;\n\tif (path.isAbsolute(relative)) {\n\t\tthrow new ToolError(`Absolute paths are not allowed in ${scheme}:// URLs: ${rawPath}`);\n\t}","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/js/shared/helpers.ts#L98-L134","documentation":"`resolveHelperPath` rewrites internal-URL paths (e.g. `local://x.md`) to on-disk roots supplied by the host via `ctx.localRoots()`. If the path uses an internal scheme that has no registered root (or is not an internal URL at all and does not resolve as a normal path), the helper refuses rather than guess a location. This keeps sandbox file I/O confined to host-approved directories.","triggerScenarios":"Calling `read()`/`write()` with a path like `scheme://file` where `scheme` is not present in the localRoots map for this session (e.g. using `artifact://` or `local://` when the session only registered `local`), or any URL-looking path that fails the INTERNAL_URL_RE and also fails normal resolution.","commonSituations":"Using an internal protocol the eval session never registered; typo in the scheme name; running eval code outside the host context that mounts local roots; version change renaming a scheme.","solutions":["Use a scheme that the session registers in localRoots (check the eval tool's localRoots option, e.g. `local`).","Pass a plain relative or absolute filesystem path instead of a protocol URL if you do not need internal-root mapping.","Register the scheme's root in the session config (localRoots) before running eval code that references it."],"exampleFix":"// before\nconst txt = await read(\"artifact://report.md\"); // scheme not registered\n// after\nconst txt = await read(\"local://report.md\"); // 'local' is a registered root","handlingStrategy":"validation","validationCode":"const m = /^(\\w+):\\/\\//.exec(p);\nif (m && !(m[1] in registeredLocalRoots)) throw new Error(`unknown scheme: ${m[1]}`);","typeGuard":null,"tryCatchPattern":"try {\n\treturn await read(path);\n} catch (err) {\n\tif (String(err?.message).startsWith(\"Protocol paths are not supported\")) {\n\t\treturn await read(path.replace(/^\\w+:\\/\\//, \"\")); // retry as plain path\n\t}\n\tthrow err;\n}","preventionTips":["Only use schemes listed in the session's localRoots config (typically `local`).","Prefer plain relative/absolute paths unless you specifically need root mapping.","Register every scheme your eval scripts reference before running them."],"tags":["path-resolution","sandbox","validation","file-io"],"backgroundTag":"unsupported-protocol-path","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}