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

  1. 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.
  2. Restrict write actions to file:// / workspace paths and keep internal URLs read-only.
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/1ab0f2c846bf2879. Report an issue: GitHub.