can1357/oh-my-pi · error · ParseError

Unexpected line in hunk: '${line}'. Lines must start with '

Error message

Unexpected line in hunk: '${line}'. Lines must start with ' ' (context), '+' (add), or '-' (remove)

What it means

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.

Source

Thrown at packages/coding-agent/src/edit/diff.ts:704

		if (firstChar === undefined || firstChar === "") {
			hunk.hasContextLines = true;
			hunk.oldLines.push("");
			hunk.newLines.push("");
		} else if (firstChar === " ") {
			hunk.hasContextLines = true;
			hunk.oldLines.push(line.slice(1));
			hunk.newLines.push(line.slice(1));
		} else if (firstChar === "+") {
			hunk.newLines.push(line.slice(1));
		} else if (firstChar === "-") {
			hunk.oldLines.push(line.slice(1));
		} else if (!line.startsWith("@@")) {
			hunk.hasContextLines = true;
			hunk.oldLines.push(line);
			hunk.newLines.push(line);
		} else {
			if (parsedLines === 0) {
				throw new ParseError(
					`Unexpected line in hunk: '${line}'. Lines must start with ' ' (context), '+' (add), or '-' (remove)`,
					lineNumber + 1,
				);
			}
			break;
		}
		parsedLines++;
	}

	if (parsedLines === 0) {
		throw new ParseError("Hunk does not contain any lines", lineNumber + startIndex);
	}

	stripLineNumberPrefixes(hunk);
	return { hunk, linesConsumed: parsedLines + startIndex };
}

function stripLineNumberPrefixes(hunk: DiffHunk): void {

View on GitHub (pinned to 9690622007)

Solutions

  1. Prefix every body line correctly: keep leading space for context, '+' for additions, '-' for removals.
  2. Remove stray text/second '@@' headers from the hunk body.
  3. Re-copy the patch in a way that preserves leading whitespace (disable 'quote prefixing' and reflow).
  4. Pre-scan body lines with /^( |\+|-|\\|$)/ and reject/reject-fix before parsing.

Example fix

// before
@@ -1,2 +1,2 @@
this line lost its prefix
+added
// after
@@ -1,2 +1,2 @@
 this line lost its prefix
+added
Defensive patterns

Strategy: validation

Validate before calling

const body = diff.split("\n").filter(l => !l.startsWith("@@"));
const bad = body.find(l => l !== "" && !/^[ +-\\]/.test(l));
if (bad !== undefined) {
  throw new Error(`Diff body line lacks ' '/'+'/'-' prefix: ${JSON.stringify(bad.slice(0, 40))}`);
}

Try / catch

try {
  hunks = parseDiffHunks(diff);
} catch (err) {
  if (err instanceof ParseError && err.message.startsWith("Unexpected line in hunk")) {
    // the message quotes the bad line; fix its prefix or remove it, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/10cce8a238069bf3. Report an issue: GitHub.