can1357/oh-my-pi · error · ToolError

path entry "${entry}" — only line-range selectors like ":50-

Error message

path entry "${entry}" — only line-range selectors like ":50-100" are supported (no ":raw"/":conflicts")

What it means

In the non-internal-URL branch, parsePathSpecs parses the ':sel' suffix with parseLineRanges; grep only supports line-range selectors, so ':raw' or ':conflicts' (read-only selectors) fail to parse and are rejected with this error.

Source

Thrown at packages/coding-agent/src/tools/grep.ts:189

			if (!isReadSelectorGrammar(internalSplit.sel)) {
				throw new ToolError(
					`path entry "${entry}" has an invalid selector ":${internalSplit.sel}" — use ":N-M" line ranges, ":raw"/":conflicts", a range plus ":raw", or percent-encode a literal ":" as %3A`,
				);
			}
			specs.push({ original: entry, clean: internalSplit.path, ranges: selectorLineRanges(internalSplit.sel) });
			continue;
		}
		// Prefer a literal filesystem match when one exists — a real file named
		// `test:1-2` outranks the `:1-2` selector interpretation (issue #4618).
		const strictSplit = splitPathAndSel(entry);
		const split = await splitPathAndSelPreferringLiteral(entry, cwd);
		const literalFilesystemMatch = strictSplit.sel !== undefined && split.sel === undefined;
		let clean = literalFilesystemMatch ? resolveReadPath(entry, cwd) : entry;
		let ranges: [LineRange, ...LineRange[]] | undefined;
		if (!literalFilesystemMatch && split.sel) {
			const parsed = parseLineRanges(split.sel);
			if (!parsed) {
				throw new ToolError(
					`path entry "${entry}" — only line-range selectors like ":50-100" are supported (no ":raw"/":conflicts")`,
				);
			}
			if (hasGlobPathChars(split.path)) {
				throw new ToolError(`Line-range selector requires a single file, not a glob: ${entry}`);
			}
			clean = split.path;
			ranges = parsed;
		}
		specs.push({
			original: entry,
			clean,
			literalFilesystemMatch,
			ranges,
		});
	}
	return specs;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Remove ':raw'/':conflicts' from the path entry and pass only a plain path or ':N-M' ranges
  2. Use the read tool if you need :raw/:conflicts selectors
  3. Search the whole file with grep and read specific chunks separately

Example fix

// before
await grepTool.execute({ pattern: "foo", path: "src/app.ts:raw" })
// after
await grepTool.execute({ pattern: "foo", path: "src/app.ts" })
Defensive patterns

Strategy: validation

Validate before calling

function stripReadOnlySelectors(entry: string): string {
  return entry.replace(/:(raw|conflicts)$/i, "");
}
await grepTool.execute({ pattern, path: entries.map(stripReadOnlySelectors) });

Type guard

function hasReadOnlySelector(entry: string): boolean {
  return /:(raw|conflicts)(:\d+-\d+)?$/.test(entry);
}

Try / catch

try {
  return await grepTool.execute({ pattern, path: entries });
} catch (err) {
  if (err instanceof ToolError && err.message.includes(":raw\"/\":conflicts")) {
    return await grepTool.execute({ pattern, path: entries.map(e => e.split(":")[0]) });
  }
  throw err;
}

Prevention

When it happens

Trigger: Path entries like 'file.ts:raw' or 'file.ts:conflicts' passed to grep; entry resolves as a literal filesystem match (strictSplit found a selector the normal split did not) and the selector is not a line range.

Common situations: Reusing read-tool arguments for grep; assuming grep supports raw/conflict chunk selectors like the read tool.

Related errors


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