can1357/oh-my-pi · error · ParseError

Line numbers must be >= 1 (got ${oldStartLine})

Error message

Line numbers must be >= 1 (got ${oldStartLine})

What it means

Thrown after header parsing when the resolved old start line (from a unified header or a line hint) is defined but less than 1. This is a final safety net covering paths that set oldStartLine outside the unified-header branch (e.g. context line hints), enforcing 1-based line numbers before any hunk content is consumed.

Source

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

		} else if (trimmedContextValue.length > 0) {
			changeContexts.push(contextValue);
		}
		startIndex = 1;
	} else if (isHeaderLine) {
		const contextValue = headerTrimmed.slice(2).trim();
		if (contextValue.length > 0) {
			changeContexts.push(contextValue);
		}
		startIndex = 1;
	} else {
		if (!allowMissingContext) {
			throw new ParseError(`Expected hunk to start with @@ context marker, got: '${lines[0]}'`, lineNumber);
		}
		startIndex = 0;
	}

	if (oldStartLine !== undefined && oldStartLine < 1) {
		throw new ParseError(`Line numbers must be >= 1 (got ${oldStartLine})`, lineNumber);
	}
	if (newStartLine !== undefined && newStartLine < 1) {
		throw new ParseError(`Line numbers must be >= 1 (got ${newStartLine})`, lineNumber);
	}

	while (startIndex < lines.length) {
		const nextLine = lines[startIndex];
		if (!nextLine.startsWith("@@")) {
			break;
		}
		const trimmed = nextLine.trimEnd();
		if (trimmed.startsWith(CHANGE_CONTEXT_MARKER)) {
			const nestedContext = trimmed.slice(CHANGE_CONTEXT_MARKER.length);
			if (nestedContext.trim().length > 0) {
				changeContexts.push(nestedContext);
			}
			startIndex++;
		} else if (trimmed === EMPTY_CHANGE_CONTEXT_MARKER) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Renumber the old-side start line to >= 1 in the '@@' header.
  2. Use the top-of-file marker instead of a line-0 hint.
  3. Fix the upstream generator's index base (add 1).
  4. Pre-validate parsed header numbers before handing the diff to the parser.

Example fix

// before
@@ util.ts line 0
// after
@@ util.ts line 1
Defensive patterns

Strategy: validation

Validate before calling

// after extracting oldStartLine from any header form
if (oldStartLine !== undefined && oldStartLine < 1) {
  throw new Error(`old start line must be >= 1, got ${oldStartLine}`);
}

Try / catch

try {
  hunks = parseDiffHunks(diff);
} catch (err) {
  if (err instanceof ParseError && err.message.startsWith("Line numbers must be >= 1")) {
    // renumber and retry
  } else throw err;
}

Prevention

When it happens

Trigger: parseOneHunk with a header that yields oldStartLine = 0 or negative — typically an '@@' context header with a 'line 0' hint that somehow bypassed the hint-specific check, or programmatic construction of the header object.

Common situations: Generated patches using 0-based line numbers; tooling that writes '@@ line 0' meaning top-of-file; round-tripping diffs through custom formatters that renumber lines.

Related errors


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