vxcontrol/pentagi · error

diff contains no hunks (no "@@ ... @@" header found)

Error message

diff contains no hunks (no "@@ ... @@" header found)

What it means

parseUnifiedDiff in backend/pkg/tools/file_diff.go scans a unified diff for the first '@@ ... @@' hunk header. If it consumes the entire input without finding one, it reports that the diff contains no hunks. The tool requires a well-formed unified diff; input like 'old file text' or a plain English description of the edit is not accepted.

Source

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

// separate argument), "\ No newline at end of file" markers, and a fully
// blank line standing in for a one-space (empty) context line.
func parseUnifiedDiff(diffText string) ([]diffHunk, error) {
	normalized := strings.TrimSuffix(strings.ReplaceAll(diffText, "\r\n", "\n"), "\n")
	if normalized == "" {
		return nil, fmt.Errorf("diff is empty")
	}
	lines := strings.Split(normalized, "\n")

	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
		}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Inspect the diff string passed to ApplyUnifiedDiff and ensure it contains at least one '@@ -old +new @@' hunk header after the optional ---/+++ file headers.
  2. If the diff is produced by another tool or LLM, ask it to regenerate as a proper unified diff (git diff format) with hunk headers.
  3. Check the input is not empty or truncated (log its length and first lines before calling ApplyUnifiedDiff).
  4. If no change is actually needed, skip calling ApplyUnifiedDiff entirely instead of passing an empty diff.

Example fix

// before
ApplyUnifiedDiff(ctx, path, "--- a/f.txt\n+++ b/f.txt")

// after
ApplyUnifiedDiff(ctx, path, "--- a/f.txt\n+++ b/f.txt\n@@ -1,1 +1,2 @@\n old line\n+new line")
Defensive patterns

Strategy: validation

Validate before calling

func hasHunkHeader(diff string) bool {
	return strings.Contains(diff, "@@") && regexp.MustCompile(`(?m)^@@ -\d+(,\d+)? \+\d+(,\d+)? @@`).MatchString(diff)
}
// call ApplyUnifiedDiff only if hasHunkHeader(diff) && strings.TrimSpace(diff) != ""

Try / catch

newContent, n, err := ApplyUnifiedDiff(ctx, path, diff)
if err != nil {
	if strings.Contains(err.Error(), "no hunks") {
		// treat as no-op or regenerate a proper unified diff
	}
	return err
}

Prevention

When it happens

Trigger: ApplyUnifiedDiff or EditFile called with a diff string that has no '@@ -a,b +c,d @@' header at all — e.g. an empty string, a natural-language description of the change, plain search/replace text, or a header-only diff whose lines after ---/+++ are not hunk headers.

Common situations: An LLM agent returns a prose description or a '```diff' block that is actually empty; user pastes only the file-portion (---/+++) of a git diff; a template generated an empty diff when content was unchanged; diff output got truncated before the first hunk.

Related errors


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