{"record":{"id":"110a86267350de7c","repo":"can1357/oh-my-pi","slug":"diff-does-not-contain-any-lines","errorCode":null,"errorMessage":"Diff does not contain any lines","messagePattern":"Diff does not contain any lines","errorType":"validation","errorClass":"ParseError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/diff.ts","lineNumber":558,"sourceCode":"\t\tchangeContext: changeContext && changeContext.length > 0 ? changeContext : undefined,\n\t};\n}\n\nfunction isUnifiedDiffMetadataLine(line: string): boolean {\n\treturn matchesTrimmedPrefix(\n\t\tline,\n\t\tDIFF_METADATA_PREFIXES.filter(prefix => !prefix.startsWith(\"*** \")),\n\t);\n}\n\ninterface ParseHunkResult {\n\thunk: DiffHunk;\n\tlinesConsumed: number;\n}\n\nfunction parseOneHunk(lines: string[], lineNumber: number, allowMissingContext: boolean): ParseHunkResult {\n\tif (lines.length === 0) {\n\t\tthrow new ParseError(\"Diff does not contain any lines\", lineNumber);\n\t}\n\n\tconst changeContexts: string[] = [];\n\tlet oldStartLine: number | undefined;\n\tlet newStartLine: number | undefined;\n\tlet startIndex: number;\n\n\tconst headerLine = lines[0];\n\tconst headerTrimmed = headerLine.trimEnd();\n\tconst isHeaderLine = headerLine.startsWith(\"@@\");\n\tconst unifiedHeader = isHeaderLine ? parseUnifiedHunkHeader(headerTrimmed) : undefined;\n\tconst isEmptyContextMarker = /^@@\\s*@@$/.test(headerTrimmed);\n\n\tif (isHeaderLine && (headerTrimmed === EMPTY_CHANGE_CONTEXT_MARKER || isEmptyContextMarker)) {\n\t\tstartIndex = 1;\n\t} else if (unifiedHeader) {\n\t\tif (unifiedHeader.oldStartLine < 1 || unifiedHeader.newStartLine < 1) {\n\t\t\tthrow new ParseError(\"Line numbers in @@ header must be >= 1\", lineNumber);","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/diff.ts#L540-L576","documentation":"parseOneHunk in the unified-diff parser throws when asked to parse a hunk from an empty remaining line list. It is the boundary guard for a diff that ends (or a hunk header that points past the end) with no hunk body lines available.","triggerScenarios":"Calling the diff parser (via applyLegacyPatch/parse paths) on a diff whose @@ hunk header is the last line, or a diff body with zero lines — the hunk parser is invoked with lines.length === 0.","commonSituations":"Unified diff truncated right after an @@ header; empty diff file passed to the applier; a tool generated a header-only diff; line-count mismatches after CR/LF stripping left no body lines.","solutions":["Ensure the diff contains complete hunks: each @@ header followed by its body lines","Check for truncation at the source (clipboard, token limit, log capture) and regenerate the diff","Validate the diff is non-empty before invoking the applier","Catch ParseError and surface 'diff is truncated/empty' to the user"],"exampleFix":"// before\nawait applyLegacyPatch('@@ -1,1 +1,2 @@'); // header with no body\n// after\nawait applyLegacyPatch('@@ -1,1 +1,2 @@\\n-old line\\n+new line');","handlingStrategy":"validation","validationCode":"if (!diffText.includes('@@') || diffText.split('\\n').filter(l => l.trim()).length < 2) {\n\tthrow new SkipOperation('diff is empty or header-only');\n}","typeGuard":"function hasHunkBody(diffText) {\n\tconst lines = diffText.split('\\n');\n\tconst i = lines.findIndex(l => l.startsWith('@@'));\n\treturn i !== -1 && i + 1 < lines.length && /^[+\\- ]/.test(lines[i + 1]);\n}","tryCatchPattern":"try {\n\tawait applyLegacyPatch(diffText);\n} catch (err) {\n\tif (err instanceof ParseError && err.message === 'Diff does not contain any lines') {\n\t\t// surface 'diff is truncated or empty'\n\t}\n}","preventionTips":["Verify diffs contain complete hunks before applying","Check for truncation at capture time (clipboard, token limits)","Reject empty diffs early in tool pipelines"],"tags":["diff","parser","unified-diff","empty-input"],"backgroundTag":"malformed-diff","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}