{"record":{"id":"0336d119842d2c29","repo":"can1357/oh-my-pi","slug":"line-anchor-line-does-not-exist-file-has-fil","errorCode":null,"errorMessage":"Line ${anchor.line} does not exist (file has ${fileLines.length} lines)","messagePattern":"Line (.+?) does not exist \\(file has (.+?) lines\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/hashline/src/apply.ts","lineNumber":85,"sourceCode":"\t// thing and delete through the last concrete line.\n\treturn fileLines.length > 1 && fileLines[fileLines.length - 1] === \"\" ? fileLines.length : 0;\n}\n\nfunction dropTrailingPhantomDeletes(edits: AppliedEdit[], fileLines: readonly string[]): AppliedEdit[] {\n\tconst phantomLine = trailingPhantomLine(fileLines);\n\tif (phantomLine === 0) return edits;\n\treturn edits.filter(edit => edit.kind !== \"delete\" || edit.anchor.line !== phantomLine);\n}\n\n/**\n * Verify every anchored edit points at an existing line. File-version binding is\n * checked once per section via the header hash before this function runs.\n */\nfunction validateLineBounds(edits: readonly AppliedEdit[], fileLines: readonly string[]): void {\n\tfor (const edit of edits) {\n\t\tfor (const anchor of getEditAnchors(edit)) {\n\t\t\tif (anchor.line < 1 || anchor.line > fileLines.length) {\n\t\t\t\tthrow new Error(`Line ${anchor.line} does not exist (file has ${fileLines.length} lines)`);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction cloneAppliedEdit(edit: AppliedEdit, index: number): AppliedEdit {\n\tif (edit.kind === \"delete\") return { ...edit, anchor: { ...edit.anchor }, index };\n\treturn { ...edit, cursor: cloneCursor(edit.cursor), index };\n}\n\nfunction insertAtStart(fileLines: string[], lineOrigins: LineOrigin[], lines: string[]): void {\n\tif (lines.length === 0) return;\n\tconst origins = lines.map((): LineOrigin => \"insert\");\n\tif (fileLines.length === 1 && fileLines[0] === \"\") {\n\t\tfileLines.splice(0, 1, ...lines);\n\t\tlineOrigins.splice(0, 1, ...origins);\n\t\treturn;\n\t}","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/hashline/src/apply.ts#L67-L103","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait applyEdits(path, edits); // throws if line 500 gone\n// after\nconst lines = (await Bun.file(path).text()).split(\"\\n\");\nconst safe = edits.filter(e => getEditAnchors(e).every(a => a.line >= 1 && a.line <= lines.length));\nawait applyEdits(path, safe);","handlingStrategy":"validation","validationCode":"const lines = (await Bun.file(path).text()).split(\"\\n\");\nfor (const e of edits) {\n  for (const a of getEditAnchors(e)) {\n    if (a.line < 1 || a.line > lines.length) {\n      throw new Error(`stale anchor: line ${a.line} not in 1..${lines.length}`);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await applyEdits(path, edits);\n} catch (e) {\n  if (e.message.includes(\"does not exist\")) {\n    // re-read file and regenerate the patch with fresh line numbers\n  }\n}","preventionTips":["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"],"tags":["patching","validation","line-numbers","editing"],"backgroundTag":"line-number-out-of-range","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}