{"record":{"id":"e3e31a00030f9348","repo":"can1357/oh-my-pi","slug":"hunk-does-not-contain-any-lines","errorCode":null,"errorMessage":"Hunk does not contain any lines","messagePattern":"Hunk does not contain any lines","errorType":"validation","errorClass":"ParseError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/diff.ts","lineNumber":643,"sourceCode":"\t\tif (!nextLine.startsWith(\"@@\")) {\n\t\t\tbreak;\n\t\t}\n\t\tconst trimmed = nextLine.trimEnd();\n\t\tif (trimmed.startsWith(CHANGE_CONTEXT_MARKER)) {\n\t\t\tconst nestedContext = trimmed.slice(CHANGE_CONTEXT_MARKER.length);\n\t\t\tif (nestedContext.trim().length > 0) {\n\t\t\t\tchangeContexts.push(nestedContext);\n\t\t\t}\n\t\t\tstartIndex++;\n\t\t} else if (trimmed === EMPTY_CHANGE_CONTEXT_MARKER) {\n\t\t\tstartIndex++;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (startIndex >= lines.length) {\n\t\tthrow new ParseError(\"Hunk does not contain any lines\", lineNumber + 1);\n\t}\n\n\tconst changeContext = changeContexts.length > 0 ? changeContexts.join(\"\\n\") : undefined;\n\n\tconst hunk: DiffHunk = {\n\t\tchangeContext,\n\t\toldStartLine,\n\t\tnewStartLine,\n\t\thasContextLines: false,\n\t\toldLines: [],\n\t\tnewLines: [],\n\t\tisEndOfFile: false,\n\t};\n\n\tlet parsedLines = 0;\n\n\tfor (let i = startIndex; i < lines.length; i++) {\n\t\tconst line = lines[i];","sourceCodeStart":625,"sourceCodeEnd":661,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/diff.ts#L625-L661","documentation":"Thrown when, after consuming the '@@' header and any nested change-context lines, the parser has run past the end of the input without finding a single content line. A hunk header with no body is meaningless — there is nothing to apply — so the parser rejects it, reporting the first missing content position (header line + 1).","triggerScenarios":"Calling parseDiffHunks on input that ends immediately after an '@@ ...' header (or after header + context markers), e.g. a diff string ending in '@@ -1,3 +1,3 @@' with no following +/-/space lines.","commonSituations":"Truncated patches from interrupted file writes or clipped LLM output; copy/paste that dropped the body; templates where the body variable was empty.","solutions":["Append the missing hunk body (at least one ' ', '+', or '-' line) after the header.","Check the diff source for truncation — verify the patch file/stream is complete.","If the hunk is intentionally empty, remove the header entirely rather than shipping a bare '@@' line.","Validate that diff.split('\\n') has content after each header before parsing."],"exampleFix":"// before\n@@ -1,3 +1,3 @@\n<end of diff>\n// after\n@@ -1,3 +1,3 @@\n-old value\n+new value","handlingStrategy":"validation","validationCode":"const lines = diff.split(\"\\n\");\nlines.forEach((l, i) => {\n  if (l.startsWith(\"@@\") && (lines[i + 1] === undefined || lines[i + 1].startsWith(\"@@\"))) {\n    throw new Error(`Header at diff line ${i + 1} has no body`);\n  }\n});","typeGuard":null,"tryCatchPattern":"try {\n  hunks = parseDiffHunks(diff);\n} catch (err) {\n  if (err instanceof ParseError && err.message === \"Hunk does not contain any lines\") {\n    // check patch source for truncation; request a regenerated patch\n  } else throw err;\n}","preventionTips":["Verify patch completeness (e.g. compare byte count or checksum) before parsing.","Watch for output caps clipping LLM-generated diffs mid-hunk.","Drop intentionally-empty hunks instead of shipping bare headers."],"tags":["diff","parsing","truncated-input"],"backgroundTag":"empty-hunk-body","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}