can1357/oh-my-pi · error · ParseError

Line numbers in @@ header must be >= 1

Error message

Line numbers in @@ header must be >= 1

What it means

This ParseError is thrown by parseOneHunk when a hunk starts with a standard unified-diff '@@ -a,b +c,d @@' header whose old or new start line number is less than 1. The parser requires every line number in the header to be a positive 1-based index; 0 or negative values are invalid in unified diffs. The library throws so callers get a precise, line-attributed message instead of applying a patch at a bogus location.

Source

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

		throw new ParseError("Diff does not contain any lines", lineNumber);
	}

	const changeContexts: string[] = [];
	let oldStartLine: number | undefined;
	let newStartLine: number | undefined;
	let startIndex: number;

	const headerLine = lines[0];
	const headerTrimmed = headerLine.trimEnd();
	const isHeaderLine = headerLine.startsWith("@@");
	const unifiedHeader = isHeaderLine ? parseUnifiedHunkHeader(headerTrimmed) : undefined;
	const isEmptyContextMarker = /^@@\s*@@$/.test(headerTrimmed);

	if (isHeaderLine && (headerTrimmed === EMPTY_CHANGE_CONTEXT_MARKER || isEmptyContextMarker)) {
		startIndex = 1;
	} else if (unifiedHeader) {
		if (unifiedHeader.oldStartLine < 1 || unifiedHeader.newStartLine < 1) {
			throw new ParseError("Line numbers in @@ header must be >= 1", lineNumber);
		}
		if (unifiedHeader.changeContext) {
			changeContexts.push(unifiedHeader.changeContext);
		}
		oldStartLine = unifiedHeader.oldStartLine;
		newStartLine = unifiedHeader.newStartLine;
		startIndex = 1;
	} else if (isHeaderLine && headerTrimmed.startsWith(CHANGE_CONTEXT_MARKER)) {
		const contextValue = headerTrimmed.slice(CHANGE_CONTEXT_MARKER.length);
		const trimmedContextValue = contextValue.trim();
		const normalizedContextValue = trimmedContextValue.replace(/^@@\s*/u, "");

		const lineHintMatch = normalizedContextValue.match(LINE_HINT_REGEX);
		if (lineHintMatch) {
			oldStartLine = Number(lineHintMatch[1]);
			newStartLine = oldStartLine;
			if (oldStartLine < 1) {
				throw new ParseError("Line hint must be >= 1", lineNumber);

View on GitHub (pinned to 9690622007)

Solutions

  1. Renumber the hunk header so both start lines are >= 1 (new/empty files typically use '+1,N').
  2. If the patch targets a brand-new file, start the hunk at line 1 rather than 0.
  3. Regenerate the patch with the producing tool instead of hand-editing the header.
  4. Wrap the parse in try-catch for ParseError and surface the failing line number to the user.

Example fix

// before
@@ -0,0 +1,3 @@
+new line
// after
@@ -1,3 +1,3 @@
 new line
Defensive patterns

Strategy: validation

Validate before calling

const m = firstLine.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
if (m && (Number(m[1]) < 1 || Number(m[2]) < 1)) {
  throw new Error(`Hunk header start lines must be >= 1, got -${m[1]} +${m[2]}`);
}

Try / catch

try {
  hunks = parseDiffHunks(diff);
} catch (err) {
  if (err instanceof ParseError && err.message.includes("must be >= 1")) {
    // surface err.lineNumber and the offending header to the user / fix generator
  } else throw err;
}

Prevention

When it happens

Trigger: Calling parseDiffHunks or parseOneHunk with a diff whose first line matches UNIFIED_HUNK_HEADER_REGEX but contains a start line of 0 or a negative number, e.g. '@@ -0,0 +1,3 @@' or '@@ -5 +0 @@'.

Common situations: Hand-written patches where the author counts lines from 0; diffs generated against an empty/new file and mis-computed by custom tooling; tools that emit '@@ -0,0' for file-creation hunks (valid in git but rejected here since it implies insert-at-zero).

Related errors


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