golangci/golangci-lint · error

%q suggests invalid fix: pos (%v) > end (%v)

Error message

%q suggests invalid fix: pos (%v) > end (%v)

What it means

The fixer applies SuggestedFixes from linters, and each text edit must have Pos <= End. When a linter emits a suggested fix whose start position is after its end position, the fixer rejects the whole run's fixes with this error naming the linter. It guards against corrupting files with inverted edit ranges.

Source

Thrown at pkg/result/processors/fixer.go:106

	for i := range issues {
		issue := issues[i]

		if slices.Contains(formatters, issue.FromLinter) {
			toBeFormattedFiles[issue.FilePath()] = struct{}{}
			continue
		}

		if issue.SuggestedFixes == nil || skipNoTextEdit(issue) {
			notFixableIssues = append(notFixableIssues, issue)
			continue
		}

		for _, sf := range issue.SuggestedFixes {
			for _, edit := range sf.TextEdits {
				start, end := edit.Pos, edit.End
				if start > end {
					return nil, fmt.Errorf("%q suggests invalid fix: pos (%v) > end (%v)",
						issue.FromLinter, edit.Pos, edit.End)
				}

				edit := diff.Edit{
					Start: int(start),
					End:   int(end),
					New:   string(edit.NewText),
				}

				if _, ok := editsByLinter[issue.FilePath()]; !ok {
					editsByLinter[issue.FilePath()] = make(map[string][]diff.Edit)
				}

				editsByLinter[issue.FilePath()][issue.FromLinter] = append(editsByLinter[issue.FilePath()][issue.FromLinter], edit)
			}
		}
	}

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Update (or pin) the offending linter named in the error to a version with fixed position computation
  2. Disable the linter's fixer for that linter (e.g. run without --fix or exclude the linter) until fixed
  3. Report the bug to the linter maintainer with the failing file/linter name
  4. Verify file/position data isn't being transformed between analysis and fixing in custom plugins

Example fix

// before (custom linter)
edit := analysis.TextEdit{Pos: end, End: start}
// after
if start > end { start, end = end, start }
edit := analysis.TextEdit{Pos: start, End: end}
Defensive patterns

Strategy: validation

Validate before calling

// Go: sanity-check text edits before emitting a SuggestedFix from a custom linter
func validEdit(e analysis.TextEdit) bool { return e.Pos <= e.End }
if !validEdit(edit) { return nil, fmt.Errorf("invalid edit range %d..%d", edit.Pos, edit.End) }

Try / catch

fixed, err := fixer.Process(issues)
if err != nil {
    if strings.Contains(err.Error(), "suggests invalid fix") {
        log.Fatalf("linter emitted bad fix: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: process iterates issue.SuggestedFixes and encounters a TextEdit where edit.Pos > edit.End — produced by a buggy linter or an analyzer working with stale/incorrect position information.

Common situations: A custom or third-party linter computing token positions off-by-one; using a linter version incompatible with the golangci-lint go/analysis version; fixes generated from cached/rewritten file positions after edits.


AI-assisted analysis of golangci/golangci-lint@ed7a235d2d (2026-09-02). Data as JSON: /api/errors/5aed7e67b92d0fde. Report an issue: GitHub.