can1357/oh-my-pi · error · ToolError

Reading `conflict://*` is not supported — wildcards are writ

Error message

Reading `conflict://*` is not supported — wildcards are write-only. Use the `<path>:conflicts` read selector for the full list of conflicts in a file, or read `conflict://<N>` to inspect a singl

What it means

conflict:// URIs address file-conflict blocks. The '*' id is write-only — it exists for resolving/writing all conflicts — and reading it is rejected with a ToolError directing you to the '<path>:conflicts' read selector for the full list or 'conflict://<N>' for a single block. This keeps bulk reads from dumping every conflict block at once.

Source

Thrown at packages/coding-agent/src/tools/read.ts:1107

			readPath = expandPath(readPath);
		}

		if (IMAGE_ATTACHMENT_URI_REGEX.test(readPath)) {
			const attachments = this.session.getImageAttachments?.() ?? [];
			const attachment = attachments.find(entry => entry.uri === readPath);
			if (!attachment) {
				const availableUris = attachments.map(entry => entry.uri).join(", ") || "none";
				throw new ToolError(
					`Could not resolve image attachment '${readPath}'. Available attachment URIs: ${availableUris}. Use one of the listed attachment URIs, or attach an image first when none are available.`,
				);
			}
			readPath = attachment.sourcePath;
		}

		const conflictUri = parseConflictUri(readPath);
		if (conflictUri) {
			if (conflictUri.id === "*") {
				throw new ToolError(
					"Reading `conflict://*` is not supported — wildcards are write-only. Use the `<path>:conflicts` read selector for the full list of conflicts in a file, or read `conflict://<N>` to inspect a single block.",
				);
			}
			return this.#readConflictRegion(conflictUri.id, conflictUri.scope);
		}
		const displayMode = resolveFileDisplayMode(this.session);

		const parsedUrlTarget = parseReadUrlTarget(readPath);
		if (parsedUrlTarget) {
			if (!this.session.settings.get("fetch.enabled")) {
				throw new ToolError("URL reads are disabled by settings.");
			}
			const urlRaw = parsedUrlTarget.raw;
			const urlRanges = parsedUrlTarget.ranges;
			if (urlRanges !== undefined && urlRanges.length > 1) {
				const entry = await fetchReadUrl(this.session, { path: parsedUrlTarget.path, raw: urlRaw }, signal, {
					ensureArtifact: true,
				});

View on GitHub (pinned to 9690622007)

Solutions

  1. Use '<path>:conflicts' to list all conflicts in a file.
  2. Read one block at a time with 'conflict://<N>' using ids from that list.
  3. Resolve conflicts via the write path (wildcard) if the goal is resolution, not inspection.

Example fix

// before
read('conflict://*')
// after
read('src/app.ts:conflicts')   // list all conflict blocks
read('conflict://2')           // inspect block 2
Defensive patterns

Strategy: validation

Validate before calling

const m = readPath.match(/^conflict:\/\/(.+)$/);
if (m && m[1] === '*') {
  // wildcard is write-only: use the file's :conflicts selector to enumerate
  throw new Error(`Use '<path>:conflicts' to list conflicts, not ${readPath}`);
}

Try / catch

try {
  return await readTool.execute({ path: target });
} catch (e) {
  if (e instanceof ToolError && target === 'conflict://*') {
    const [file] = conflictFiles; // file you intended to inspect
    return readTool.execute({ path: `${file}:conflicts` }); // list, then conflict://<N>
  }
  throw e;
}

Prevention

When it happens

Trigger: read('conflict://*') or read('conflict://path:*') used as a read target; model guessing the wildcard form after seeing conflict-resolution writes.

Common situations: Trying to enumerate conflicts in one shot; confusion between the write-side wildcard semantics and read-side selectors; exploration after a merge/edit conflict was flagged.

Related errors


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