vxcontrol/pentagi · error

invalid hunk header: %q

Error message

invalid hunk header: %q

What it means

Once parseUnifiedDiff reaches what should be a hunk header, it matches the line against unifiedDiffHunkHeaderRe. A line that fails the regex (e.g. '@@ -abc +def @@' or a stray line inside the diff body) causes this error. It is a strict format check on the '@@ -old +new @@' syntax.

Source

Thrown at backend/pkg/tools/file_diff.go:76

	i := 0
	for i < len(lines) && !strings.HasPrefix(lines[i], "@@") {
		trimmed := strings.TrimSpace(lines[i])
		if trimmed != "" && !strings.HasPrefix(trimmed, "---") && !strings.HasPrefix(trimmed, "+++") {
			return nil, fmt.Errorf("expected a hunk header (\"@@ -old +new @@\") but found: %q", lines[i])
		}
		i++
	}
	if i >= len(lines) {
		return nil, fmt.Errorf(`diff contains no hunks (no "@@ ... @@" header found)`)
	}

	var hunks []diffHunk
	for i < len(lines) {
		header := lines[i]
		m := unifiedDiffHunkHeaderRe.FindStringSubmatch(header)
		if m == nil {
			return nil, fmt.Errorf("invalid hunk header: %q", header)
		}
		hunk := diffHunk{header: header, oldStart: 1}
		if m[1] != "" {
			oldStart, err := strconv.Atoi(m[1])
			if err != nil {
				return nil, fmt.Errorf("invalid hunk header %q: %w", header, err)
			}
			hunk.oldStart = oldStart
			hunk.hasPosition = true
		}
		i++

		for i < len(lines) && !strings.HasPrefix(lines[i], "@@") {
			line := lines[i]
			i++

			if strings.HasPrefix(line, `\`) {
				// e.g. "\ No newline at end of file" - not a content line.

View on GitHub (pinned to ea665308ba)

Solutions

  1. Look at the quoted header in the error and fix its syntax to '@@ -<oldStart>[,<oldCount>] +<newStart>[,<newCount>] @@'.
  2. Regenerate the diff with a real diff tool (git diff -U3) rather than hand-writing hunk headers.
  3. Verify no unrelated lines slipped between the file header and the first hunk header.
  4. Check line endings — headers split across '\r\n' boundaries may fail matching; normalize to '\n'.

Example fix

// before
"@@ -one,2 +1,2 @@"

// after
"@@ -1,2 +1,2 @@"
Defensive patterns

Strategy: validation

Validate before calling

var hunkHeaderRe = regexp.MustCompile(`^@@ -\d+(,\d+)? \+\d+(,\d+)? @@`)
func headersValid(diff string) bool {
	for _, line := range strings.Split(diff, "\n") {
		if strings.HasPrefix(line, "@@") && !hunkHeaderRe.MatchString(line) {
			return false
		}
	}
	return true
}

Try / catch

if _, _, err := ApplyUnifiedDiff(ctx, path, diff); err != nil {
	var badHeader string
	if m := regexp.MustCompile(`invalid hunk header: "([^"]*)"`).FindStringSubmatch(err.Error()); m != nil {
		badHeader = m[1] // surface to user/regeneration prompt
	}
	return fmt.Errorf("diff rejected (bad header %q): %w", badHeader, err)
}

Prevention

When it happens

Trigger: A line between file headers and body was expected to be a hunk header but is not — e.g. '@@ -x +y @@' with non-numeric line numbers, a truncated header like '@@ -1 +1', or a random content line after the ---/+++ preamble.

Common situations: LLM-generated diffs with hand-typed hunk headers (wrong numbers of fields, 'l' instead of '1'); diffs copied from logs where header characters were mangled; tools emitting context-diff format instead of unified format.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/5aa9aac2ba35a075. Report an issue: GitHub.