Skip to content

errstringmatch coverage gap: only strings.Contains(err.Error(), ...) is flagged — HasPrefix/HasSuffix/EqualFold/Index on err.Err [Content truncated due to length] #40244

Description

@github-actions

Summary

The errstringmatch linter (pkg/linters/errstringmatch/errstringmatch.go) exists to flag brittle substring matching on error messages and steer callers toward errors.Is/errors.As/sentinel errors. Its own doc string says it reports calls that "perform brittle substring matching on error messages". But the implementation only matches strings.Contains (isStringsContains, line 76-86). The same anti-pattern expressed with other strings functions on err.Error() is just as brittle and is silently missed.

The gap (pattern-set too narrow)

The nodeFilter and pkg-identity logic are fine; the matched function set is the problem. Equally brittle, equally unflagged:

strings.HasPrefix(err.Error(), "connection refused")
strings.HasSuffix(err.Error(), "not found")
strings.EqualFold(err.Error(), "timeout")
strings.Index(err.Error(), "denied") >= 0
strings.Contains(strings.ToLower(err.Error()), "...")  // nested

Each matches an error by its human-readable message text, which breaks when the wrapped/underlying error wording changes — exactly the failure errstringmatch is meant to prevent. Only Contains is caught today.

Distinction from the package-identity issue

This is a different class from the ident.Name == "strings" syntactic-identity gap (tracked separately): that one is about how the strings package is resolved; this one is about which strings functions count as brittle error matching. Both touch errstringmatch.go but are orthogonal and should be fixed independently.

Current impact

Latent: a scan of pkg/ finds 0 production sites using HasPrefix/HasSuffix/EqualFold/Index/Compare on err.Error() today (12 strings.Contains(err.Error(), ...) sites exist and are handled). So this is a false-negative hardening finding — closing it before such code lands keeps the CI-enforced guard complete rather than fixing a present violation.

Recommendation

Generalize isStringsContains to an isBrittleErrStringFunc that recognizes the brittle strings matching functions, keeping the existing err.Error() + string-literal argument guards. Suggested set: Contains, HasPrefix, HasSuffix, EqualFold, Index, LastIndex, Compare. Note that HasPrefix/HasSuffix/EqualFold take the searched string as the first arg and the literal as the second — same shape as Contains (outer.Args[0] = err.Error(), outer.Args[1] = literal), so the existing arg checks (lines 56-63) transfer directly. Index/Compare differ only in that the brittle comparison is on the result; matching the call itself is still the right signal.

Tune the diagnostic message to name the matched function, e.g. "avoid strings.%s(err.Error(), ...) — use errors.Is, errors.As, or a sentinel error instead".

Validation checklist

  • testdata: add positive cases for HasPrefix/HasSuffix/EqualFold/Index on err.Error() with a string literal — confirm flagged.
  • testdata: keep a negative case where the first arg is NOT err.Error() (e.g. a plain string) — confirm not flagged (no over-broad matching of ordinary strings.HasPrefix).
  • Existing strings.Contains(err.Error(), ...) expectations unchanged.
  • go test ./pkg/linters/errstringmatch/... green; CI linter pass over pkg/ produces no new diagnostics (expected, given 0 latent sites).

Effort

Small — widen one predicate to a small allow-list of function names plus testdata; existing argument/pkg-identity guards reused. Single PR. Coordinate the second-arg shape only if Index/Compare are included.

Related

Separate from the package-identity migration that also touches errstringmatch.go:85. Same linter, orthogonal precision dimension.

Generated by 🤖 Sergo - Serena Go Expert · 339.2 AIC · ⌖ 14.7 AIC · ⊞ 5.8K ·

  • expires on Jun 25, 2026, 9:36 PM UTC-08:00

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions