vxcontrol/pentagi · error
hunk %q has no content lines
Error message
hunk %q has no content lines
What it means
A hunk header was found but zero body lines followed it before the next header or end of input. A '@@ ... @@' line must be followed by at least one content line; an empty hunk is rejected because it would produce a no-op or malformed patch.
Source
Thrown at backend/pkg/tools/file_diff.go:110
if strings.HasPrefix(line, `\`) {
// e.g. "\ No newline at end of file" - not a content line.
continue
}
if line == "" {
hunk.lines = append(hunk.lines, diffHunkLine{sign: ' ', text: ""})
continue
}
sign := line[0]
if sign != ' ' && sign != '-' && sign != '+' {
return nil, fmt.Errorf("invalid diff line (must start with ' ', '-', or '+'): %q", line)
}
hunk.lines = append(hunk.lines, diffHunkLine{sign: sign, text: line[1:]})
}
if len(hunk.lines) == 0 {
return nil, fmt.Errorf("hunk %q has no content lines", header)
}
hunks = append(hunks, hunk)
}
return hunks, nil
}
// buildLineOffsets returns, for content, the byte offset at which each
// 1-based line begins: offsets[0] is the (always 0) offset of line 1,
// offsets[1] of line 2, and so on.
func buildLineOffsets(content string) []int {
offsets := make([]int, 1, strings.Count(content, "\n")+1)
offsets[0] = 0
for i := 0; i < len(content); i++ {
if content[i] == '\n' {
offsets = append(offsets, i+1)
}
}View on GitHub (pinned to ea665308ba)
Solutions
- Ensure each hunk header in the diff is followed by at least one context/addition/removal line.
- Remove stray headers that have no associated body (usually leftover from a generator bug).
- Check any post-processing (trimming, filtering) that may have dropped the body lines.
- If the change is genuinely empty, do not emit a diff at all.
Example fix
// before "@@ -1,1 +1,1 @@\n@@ -2,1 +2,1 @@\n-old\n+new" // after "@@ -1,1 +1,1 @@\n-old\n+new\n@@ -2,1 +2,1 @@\n context\n+added"
Defensive patterns
Strategy: validation
Validate before calling
func hunksHaveBody(diff string) bool {
headers := 0
bodyAfterLastHeader := false
for _, line := range strings.Split(diff, "\n") {
if strings.HasPrefix(line, "@@") {
if headers > 0 && !bodyAfterLastHeader { return false }
headers++; bodyAfterLastHeader = false
} else if headers > 0 && strings.ContainsAny(line[:min(1, len(line))+0], " -+") {
bodyAfterLastHeader = true
}
}
return headers == 0 || bodyAfterLastHeader
} Try / catch
if _, _, err := ApplyUnifiedDiff(ctx, path, diff); err != nil {
if strings.Contains(err.Error(), "has no content lines") {
// drop the empty hunk and re-apply the remainder
}
return err
} Prevention
- In diff generators, always append the collected body immediately after each header
- Filter out hunks with zero body lines before applying
- Audit post-processing steps that filter or trim diff lines
When it happens
Trigger: A diff contains '@@ -5,7 +5,8 @@' immediately followed by another hunk header, EOF, or blank separator lines only — e.g. tooling that emits headers in a loop but never appends the body.
Common situations: Custom scripts that build diffs header-by-header but break on body collection; diffs where all body lines were stripped by whitespace-trimming post-processing; empty diffs for files whose only change was mode bits.
Related errors
- diff contains no hunks (no "@@ ... @@" header found)
- invalid hunk header: %q
- invalid hunk header %q: %w
- invalid diff line (must start with ' ', '-', or '+'): %q
- failed to apply diff to %s: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/465fbcb233e1575b.
Report an issue: GitHub.