can1357/oh-my-pi · error · ToolError
Path not found for line-range selector: ${spec.original}
Error message
Path not found for line-range selector: ${spec.original} What it means
A line-range selector (e.g. `file.ts:10-20` as the grep path) resolved to a path that does not exist on disk. Before applying ranges, the tool stats the cleaned path and throws this if the stat fails, differentiating "path not found" from the directory case that follows.
Source
Thrown at packages/coding-agent/src/tools/grep.ts:1080
// archive entries are keyed by scratch path, URL entries by read-cache
// content path, and ordinary files by their resolved filesystem path.
for (let idx = 0; idx < pathSpecs.length; idx++) {
const spec = pathSpecs[idx];
if (!spec.ranges) continue;
if (virtualInputIndexes.has(idx)) continue;
const resolved = internalResolution.resolvedPathsByInput[idx];
if (!resolved) continue;
const materializedExternalPath = materializedExternalPaths.get(spec.clean);
if (materializedExternalPath) {
mergeRangesInto(rangesByAbsPath, path.resolve(materializedExternalPath), spec.ranges);
continue;
}
if (resolved === spec.clean && !archiveDisplayMap.has(resolved)) {
// Non-archive entry; ensure the cleaned path resolves to a regular file.
const absKey = path.resolve(resolveReadPath(resolved, this.session.cwd));
const stats = await stat(absKey).catch(() => null);
if (!stats) {
throw new ToolError(`Path not found for line-range selector: ${spec.original}`);
}
if (!stats.isFile()) {
throw new ToolError(
`Line-range selector requires a single file: ${spec.original} is a directory`,
);
}
mergeRangesInto(rangesByAbsPath, absKey, spec.ranges);
} else {
mergeRangesInto(rangesByAbsPath, path.resolve(resolved), spec.ranges);
}
}
// When the only input was an archive selector, surface that selector instead
// of the temp scratch path the resolver substituted in.
const physicalScopePath =
searchablePaths.length === 1 && archiveDisplayMap.get(searchPath)
? (archiveDisplayMap.get(searchPath) as string)
: scope.scopePath;
scopePath = internalResolution.virtualScopePathView on GitHub (pinned to 9690622007)
Solutions
- Verify the file path exists relative to the session cwd (ls or read the file first)
- Remove the :ranges suffix and grep the directory to confirm the correct file name
- Fix the working directory or use an absolute path in the selector
Example fix
// before
await grep({ pattern: "TODO", path: "src/ap.ts:1-50" });
// after
await grep({ pattern: "TODO", path: "src/app.ts:1-50" }); Defensive patterns
Strategy: validation
Validate before calling
import * as fs from "node:fs";
const [file] = spec.split(":");
if (!fs.existsSync(file) || !fs.statSync(file).isFile()) throw new Error(`No such file: ${file}`); Prevention
- Confirm the file exists (read or ls) before attaching :start-end ranges
- Check the session cwd matches where your relative paths resolve
When it happens
Trigger: grep with a path like `src/app.ts:5-10` where src/app.ts does not exist (typo, wrong cwd, file deleted/moved, wrong branch checked out).
Common situations: See trigger scenarios.
Related errors
- Line-range selector requires a single file: ${spec.original}
- Path not found: ${missingPaths.join(", ")}; list each target
- Legacy GrepToolOptions.operations is not supported: the buil
- Path not found: ${absolutePath}
- Path not found: ${scopePath}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4a4d7c583f4f9c20.
Report an issue: GitHub.