can1357/oh-my-pi · error · Error
Line ${anchor.line} does not exist (file has ${fileLines.len
Error message
Line ${anchor.line} does not exist (file has ${fileLines.length} lines) What it means
validateLineBounds checks every edit anchor's line number against the actual file length after splitting the file into lines. If any anchor references a line below 1 or beyond EOF, applyEdits throws rather than silently skipping or corrupting the patch. Header-hash checks happen earlier, but line numbers can still drift when the on-disk file changed since the model last read it.
Source
Thrown at packages/hashline/src/apply.ts:85
// thing and delete through the last concrete line.
return fileLines.length > 1 && fileLines[fileLines.length - 1] === "" ? fileLines.length : 0;
}
function dropTrailingPhantomDeletes(edits: AppliedEdit[], fileLines: readonly string[]): AppliedEdit[] {
const phantomLine = trailingPhantomLine(fileLines);
if (phantomLine === 0) return edits;
return edits.filter(edit => edit.kind !== "delete" || edit.anchor.line !== phantomLine);
}
/**
* Verify every anchored edit points at an existing line. File-version binding is
* checked once per section via the header hash before this function runs.
*/
function validateLineBounds(edits: readonly AppliedEdit[], fileLines: readonly string[]): void {
for (const edit of edits) {
for (const anchor of getEditAnchors(edit)) {
if (anchor.line < 1 || anchor.line > fileLines.length) {
throw new Error(`Line ${anchor.line} does not exist (file has ${fileLines.length} lines)`);
}
}
}
}
function cloneAppliedEdit(edit: AppliedEdit, index: number): AppliedEdit {
if (edit.kind === "delete") return { ...edit, anchor: { ...edit.anchor }, index };
return { ...edit, cursor: cloneCursor(edit.cursor), index };
}
function insertAtStart(fileLines: string[], lineOrigins: LineOrigin[], lines: string[]): void {
if (lines.length === 0) return;
const origins = lines.map((): LineOrigin => "insert");
if (fileLines.length === 1 && fileLines[0] === "") {
fileLines.splice(0, 1, ...lines);
lineOrigins.splice(0, 1, ...origins);
return;
}View on GitHub (pinned to 9690622007)
Solutions
- Re-read the file to get fresh line numbers, then regenerate the hashline edit with valid anchors
- Clamp/verify anchor.line against the file length before calling applyEdits
- Re-run the edit workflow so the header hash and line range match the current file
- If the file shrank intentionally, rewrite it wholesale with the write tool instead of a line patch
Example fix
// before
await applyEdits(path, edits); // throws if line 500 gone
// after
const lines = (await Bun.file(path).text()).split("\n");
const safe = edits.filter(e => getEditAnchors(e).every(a => a.line >= 1 && a.line <= lines.length));
await applyEdits(path, safe); Defensive patterns
Strategy: validation
Validate before calling
const lines = (await Bun.file(path).text()).split("\n");
for (const e of edits) {
for (const a of getEditAnchors(e)) {
if (a.line < 1 || a.line > lines.length) {
throw new Error(`stale anchor: line ${a.line} not in 1..${lines.length}`);
}
}
} Try / catch
try {
await applyEdits(path, edits);
} catch (e) {
if (e.message.includes("does not exist")) {
// re-read file and regenerate the patch with fresh line numbers
}
} Prevention
- Always read the file immediately before generating line-based edits
- Regenerate patches after any external file modification
- Clamp anchor lines to the current file length in patch generators
- Prefer hash-validated sections so drift is detected early
When it happens
Trigger: Calling applyEdits with a section whose anchor line exceeds the file's line count — the file was shortened since it was read, a hashline was hand-written, or an off-by-one in generating the line number.
Common situations: Another tool/agent truncated or rewrote the file between read and edit, applying edits captured against an older file revision, or the model hallucinating a line number in a patch.
Related errors
- Line hint ${hunk.oldStartLine} is out of range for ${path} (
- Line hint ${hunk.newStartLine} is out of range for ${path} (
- Line hint ${lineHintForInsertion} is out of range for insert
- `PUT ${startLine}.=${endLine}:` rejected: a selected boundar
- MV destination is the same as ${target.path}.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0336d119842d2c29.
Report an issue: GitHub.