{"record":{"id":"10cce8a238069bf3","repo":"can1357/oh-my-pi","slug":"unexpected-line-in-hunk-line-lines-must-sta","errorCode":null,"errorMessage":"Unexpected line in hunk: '${line}'. Lines must start with ' ' (context), '+' (add), or '-' (remove)","messagePattern":"Unexpected line in hunk: '(.+?)'\\. Lines must start with ' ' \\(context\\), '\\+' \\(add\\), or '-' \\(remove\\)","errorType":"validation","errorClass":"ParseError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/diff.ts","lineNumber":704,"sourceCode":"\t\tif (firstChar === undefined || firstChar === \"\") {\n\t\t\thunk.hasContextLines = true;\n\t\t\thunk.oldLines.push(\"\");\n\t\t\thunk.newLines.push(\"\");\n\t\t} else if (firstChar === \" \") {\n\t\t\thunk.hasContextLines = true;\n\t\t\thunk.oldLines.push(line.slice(1));\n\t\t\thunk.newLines.push(line.slice(1));\n\t\t} else if (firstChar === \"+\") {\n\t\t\thunk.newLines.push(line.slice(1));\n\t\t} else if (firstChar === \"-\") {\n\t\t\thunk.oldLines.push(line.slice(1));\n\t\t} else if (!line.startsWith(\"@@\")) {\n\t\t\thunk.hasContextLines = true;\n\t\t\thunk.oldLines.push(line);\n\t\t\thunk.newLines.push(line);\n\t\t} else {\n\t\t\tif (parsedLines === 0) {\n\t\t\t\tthrow new ParseError(\n\t\t\t\t\t`Unexpected line in hunk: '${line}'. Lines must start with ' ' (context), '+' (add), or '-' (remove)`,\n\t\t\t\t\tlineNumber + 1,\n\t\t\t\t);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tparsedLines++;\n\t}\n\n\tif (parsedLines === 0) {\n\t\tthrow new ParseError(\"Hunk does not contain any lines\", lineNumber + startIndex);\n\t}\n\n\tstripLineNumberPrefixes(hunk);\n\treturn { hunk, linesConsumed: parsedLines + startIndex };\n}\n\nfunction stripLineNumberPrefixes(hunk: DiffHunk): void {","sourceCodeStart":686,"sourceCodeEnd":722,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/diff.ts#L686-L722","documentation":"Thrown when the first content line of a hunk starts with '@@' yet is not a recognized nested context marker (and parsedLines is still 0), or generally when a line inside the hunk body does not start with ' ', '+', or '-'. Every hunk body line must be context (space prefix), an addition ('+'), or a removal ('-'); anything else is structurally invalid so the parser refuses it with the offending line quoted.","triggerScenarios":"parseDiffHunks on a hunk whose body contains raw text (no prefix), markdown, a second '@@ ...' unified header immediately after the first with no content between, or lines with a corrupted first character (e.g. tab or '>' instead of space).","commonSituations":"LLM-generated patches mixing prose with diff lines; emails/forum copy where diff prefixes were reflowed or quote-prefixed ('> +code'); tools that emit two headers back-to-back; whitespace-mangling formatters.","solutions":["Prefix every body line correctly: keep leading space for context, '+' for additions, '-' for removals.","Remove stray text/second '@@' headers from the hunk body.","Re-copy the patch in a way that preserves leading whitespace (disable 'quote prefixing' and reflow).","Pre-scan body lines with /^( |\\+|-|\\\\|$)/ and reject/reject-fix before parsing."],"exampleFix":"// before\n@@ -1,2 +1,2 @@\nthis line lost its prefix\n+added\n// after\n@@ -1,2 +1,2 @@\n this line lost its prefix\n+added","handlingStrategy":"validation","validationCode":"const body = diff.split(\"\\n\").filter(l => !l.startsWith(\"@@\"));\nconst bad = body.find(l => l !== \"\" && !/^[ +-\\\\]/.test(l));\nif (bad !== undefined) {\n  throw new Error(`Diff body line lacks ' '/'+'/'-' prefix: ${JSON.stringify(bad.slice(0, 40))}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  hunks = parseDiffHunks(diff);\n} catch (err) {\n  if (err instanceof ParseError && err.message.startsWith(\"Unexpected line in hunk\")) {\n    // the message quotes the bad line; fix its prefix or remove it, then retry\n  } else throw err;\n}","preventionTips":["Copy diffs in plain-text mode so leading spaces and +/- survive.","Strip email quote prefixes ('>') and markdown artifacts before parsing.","Ask LLM generators to emit raw unified-diff bodies with no prose interleaved."],"tags":["diff","parsing","malformed-input"],"backgroundTag":"invalid-diff-line-prefix","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}