can1357/oh-my-pi · error · ParseError
Expected hunk to start with @@ context marker, got: '${lines
Error message
Expected hunk to start with @@ context marker, got: '${lines[0]}' What it means
Thrown when parseOneHunk is called with allowMissingContext=false and the first line of the candidate hunk does not start with '@@' at all. The parser expects every hunk to begin with an '@@' context/line-number marker (unified header, '@@ <context>' form, or empty '@@ @@'); anything else is rejected so content lines are never silently mis-attributed. Callers that allow context-less hunks pass allowMissingContext=true and skip this error.
Source
Thrown at packages/coding-agent/src/edit/diff.ts:611
if (oldStartLine < 1) {
throw new ParseError("Line hint must be >= 1", lineNumber);
}
} else if (TOP_OF_FILE_REGEX.test(normalizedContextValue)) {
oldStartLine = 1;
newStartLine = 1;
} 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)) {View on GitHub (pinned to 9690622007)
Solutions
- Add the missing '@@' header line at the start of the hunk (e.g. '@@@ filename @@' or a unified '@@ -a,b +c,d @@').
- Check that the diff was split into hunks/files correctly before parsing each part.
- If header-less hunks are acceptable in your pipeline, use the parse path with allowMissingContext=true.
- Inspect the first line printed in the message — it shows exactly what the parser saw.
Example fix
// before
+console.log("hi")
// after
@@ -1 +1 @@
+console.log("hi") Defensive patterns
Strategy: validation
Validate before calling
const first = diff.trimStart().split("\n")[0];
if (first !== undefined && !first.startsWith("@@")) {
throw new Error(`Hunk must start with '@@' header, got: ${JSON.stringify(first.slice(0, 40))}`);
} Try / catch
try {
hunks = parseDiffHunks(diff);
} catch (err) {
if (err instanceof ParseError && err.message.startsWith("Expected hunk to start with @@")) {
// show the offending line; ask the generator/model to re-emit with a header
} else throw err;
} Prevention
- Verify every hunk begins with an '@@' line before parsing.
- Don't split multi-hunk patches at arbitrary offsets — split only at '@@' boundaries.
- Preserve leading characters when copying diffs (avoid markdown reflow).
When it happens
Trigger: Calling parseDiffHunks on a diff whose first hunk line is raw content ('+foo', ' context', or a file marker) instead of an '@@' header; passing a stray text fragment where a hunk is expected.
Common situations: Patches missing the header because an editor stripped lines starting with '@'; models emitting only +/- lines; splitting a multi-file patch incorrectly so a content line becomes a hunk start.
Related errors
- Unexpected line in hunk: '${line}'. Lines must start with '
- Line numbers in @@ header must be >= 1
- Line hint must be >= 1
- Line numbers must be >= 1 (got ${oldStartLine})
- Line numbers must be >= 1 (got ${newStartLine})
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/21db8fdf4571a223.
Report an issue: GitHub.