can1357/oh-my-pi · error
${scheme}:// URLs are read-only for write; use the protocol-
Error message
${scheme}:// URLs are read-only for write; use the protocol-specific tool for mutations. What it means
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.
Source
Thrown at packages/coding-agent/src/internal-urls/router.ts:149
.join(", ");
throw new Error(`Unknown protocol: ${scheme}://\nSupported: ${available || "none"}`);
}
return { parsed, handler };
}
/** Resolve an internal URL through its registered protocol handler. */
async resolve(input: string, context?: ResolveContext): Promise<InternalResource> {
const { parsed, handler } = this.#route(input, true);
const resource = await handler.resolve(parsed, context);
return { ...resource, immutable: resource.immutable ?? handler.immutable };
}
/** Write an internal URL through its registered protocol handler. */
async write(input: string, content: string, context?: WriteContext): Promise<void> {
const { parsed, handler } = this.#route(input);
if (!handler.write) {
const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
throw new Error(`${scheme}:// URLs are read-only for write; use the protocol-specific tool for mutations.`);
}
await handler.write(parsed, content, context);
}
}
View on GitHub (pinned to 9690622007)
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.
Example fix
// before
await router.write('omp://docs/cli.md', newText);
// after
if (!input.startsWith('file://')) throw new Error('internal URLs are read-only; use the edit tool');
await router.write(input, newText); Defensive patterns
Strategy: validation
Validate before calling
const INTERNAL_SCHEMES = ['omp', 'memory', 'rule'];
const scheme = input.split('://')[0]?.toLowerCase();
if (scheme && INTERNAL_SCHEMES.includes(scheme)) throw new Error(`${scheme}:// is read-only; use the edit tool for mutations`); Type guard
const isReadOnlyInternal = (s: string): boolean =>
['omp', 'memory', 'rule'].includes(s.split('://')[0]?.toLowerCase() ?? ''); Try / catch
try {
await router.write(input, content);
} catch (err) {
if (err instanceof Error && err.message.includes('read-only for write')) throw new Error('use the protocol-specific tool to mutate internal resources');
throw err;
} Prevention
- 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.
When it happens
Trigger: Calling router.write(url, content) on any internal URL (omp://, memory://, rule://, etc.) whose handler does not implement write.
Common situations: Trying to edit documentation or memory artifacts via the generic internal-URL write path; wiring a generic 'write resource' action to internal URLs.
Related errors
- Artifact ${id} not found. Available: ${availableStr}
- Unknown agent: ${agentId}\nKnown agents: ${knownStr}\nList a
- Memory file not found: ${url.href}
- No documentation files found
- Absolute paths are not allowed in omp:// URLs
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1ab0f2c846bf2879.
Report an issue: GitHub.