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
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 · ◷
Summary
The
errstringmatchlinter (pkg/linters/errstringmatch/errstringmatch.go) exists to flag brittle substring matching on error messages and steer callers towarderrors.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 matchesstrings.Contains(isStringsContains, line 76-86). The same anti-pattern expressed with otherstringsfunctions onerr.Error()is just as brittle and is silently missed.The gap (pattern-set too narrow)
The
nodeFilterand pkg-identity logic are fine; the matched function set is the problem. Equally brittle, equally unflagged:Each matches an error by its human-readable message text, which breaks when the wrapped/underlying error wording changes — exactly the failure
errstringmatchis meant to prevent. OnlyContainsis 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 thestringspackage is resolved; this one is about whichstringsfunctions count as brittle error matching. Both toucherrstringmatch.gobut are orthogonal and should be fixed independently.Current impact
Latent: a scan of
pkg/finds 0 production sites usingHasPrefix/HasSuffix/EqualFold/Index/Compareonerr.Error()today (12strings.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
isStringsContainsto anisBrittleErrStringFuncthat recognizes the brittlestringsmatching functions, keeping the existingerr.Error()+ string-literal argument guards. Suggested set:Contains,HasPrefix,HasSuffix,EqualFold,Index,LastIndex,Compare. Note thatHasPrefix/HasSuffix/EqualFoldtake the searched string as the first arg and the literal as the second — same shape asContains(outer.Args[0]=err.Error(),outer.Args[1]= literal), so the existing arg checks (lines 56-63) transfer directly.Index/Comparediffer 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
HasPrefix/HasSuffix/EqualFold/Indexonerr.Error()with a string literal — confirm flagged.err.Error()(e.g. a plain string) — confirm not flagged (no over-broad matching of ordinarystrings.HasPrefix).strings.Contains(err.Error(), ...)expectations unchanged.go test ./pkg/linters/errstringmatch/...green; CI linter pass overpkg/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/Compareare included.Related
Separate from the package-identity migration that also touches
errstringmatch.go:85. Same linter, orthogonal precision dimension.