{"record":{"id":"1ab0f2c846bf2879","repo":"can1357/oh-my-pi","slug":"scheme-urls-are-read-only-for-write-use-the","errorCode":null,"errorMessage":"${scheme}:// URLs are read-only for write; use the protocol-specific tool for mutations.","messagePattern":"(.+?):// URLs are read-only for write; use the protocol-specific tool for mutations\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/router.ts","lineNumber":149,"sourceCode":"\t\t\t\t.join(\", \");\n\t\t\tthrow new Error(`Unknown protocol: ${scheme}://\\nSupported: ${available || \"none\"}`);\n\t\t}\n\t\treturn { parsed, handler };\n\t}\n\n\t/** Resolve an internal URL through its registered protocol handler. */\n\tasync resolve(input: string, context?: ResolveContext): Promise<InternalResource> {\n\t\tconst { parsed, handler } = this.#route(input, true);\n\t\tconst resource = await handler.resolve(parsed, context);\n\t\treturn { ...resource, immutable: resource.immutable ?? handler.immutable };\n\t}\n\n\t/** Write an internal URL through its registered protocol handler. */\n\tasync write(input: string, content: string, context?: WriteContext): Promise<void> {\n\t\tconst { parsed, handler } = this.#route(input);\n\t\tif (!handler.write) {\n\t\t\tconst scheme = parsed.protocol.replace(/:$/, \"\").toLowerCase();\n\t\t\tthrow new Error(`${scheme}:// URLs are read-only for write; use the protocol-specific tool for mutations.`);\n\t\t}\n\t\tawait handler.write(parsed, content, context);\n\t}\n}\n","sourceCodeStart":131,"sourceCodeEnd":154,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/router.ts#L131-L154","documentation":"router.write() delegates to the resolved protocol handler's write method; internal protocol handlers are read-only resources, so when handler.write is undefined the router throws this error telling you to use the protocol's dedicated tool for mutations.","triggerScenarios":"Calling router.write(url, content) on any internal URL (omp://, memory://, rule://, etc.) whose handler does not implement write.","commonSituations":"Trying to edit documentation or memory artifacts via the generic internal-URL write path; wiring a generic 'write resource' action to internal URLs.","solutions":["Use the protocol-specific tool instead — e.g. run a memory-enabled session for memory artifacts, or edit the underlying project files directly with the edit tool.","Restrict write actions to file:// / workspace paths and keep internal URLs read-only.","If a handler should support writes, implement handler.write in its protocol class."],"exampleFix":"// before\nawait router.write('omp://docs/cli.md', newText);\n// after\nif (!input.startsWith('file://')) throw new Error('internal URLs are read-only; use the edit tool');\nawait router.write(input, newText);","handlingStrategy":"validation","validationCode":"const INTERNAL_SCHEMES = ['omp', 'memory', 'rule'];\nconst scheme = input.split('://')[0]?.toLowerCase();\nif (scheme && INTERNAL_SCHEMES.includes(scheme)) throw new Error(`${scheme}:// is read-only; use the edit tool for mutations`);","typeGuard":"const isReadOnlyInternal = (s: string): boolean =>\n  ['omp', 'memory', 'rule'].includes(s.split('://')[0]?.toLowerCase() ?? '');","tryCatchPattern":"try {\n  await router.write(input, content);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('read-only for write')) throw new Error('use the protocol-specific tool to mutate internal resources');\n  throw err;\n}","preventionTips":["Only pass file:// (or other writable) URLs to router.write.","Keep generic write actions gated on a scheme allowlist.","Treat all internal protocol resources as immutable views."],"tags":["internal-url","read-only","write-rejected"],"backgroundTag":"read-only-resource","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}