Hi, On Fri, 15 Aug 2025 at 13:10, Oliver Eikemeier <eikeme...@fillmore-labs.com> wrote:
> Static Analysis for Go Error Type Consistency: errortype Linter > > I've been investigating a class of subtle bugs in Go error handling > related to pointer vs. value semantics with errors.As, and developed a > static analysis tool to detect these issues. I'm seeking feedback from the > community on both the approach and the tool's effectiveness. > Problem Statement > > Consider this code attempting to handle AES key size errors: > > key := []byte("My kung fu is better than yours") > _, err := aes.NewCipher(key) > > var kse *aes.KeySizeError > if errors.As(err, &kse) { > fmt.Printf("AES keys must be 16, 24 or 32 bytes long, got %d > bytes.\n", kse) > } else if err != nil { > fmt.Println(err) > } > > The bug: aes.NewCipher returns aes.KeySizeError as a value, but the code > checks for a pointer to *aes.KeySizeError. This type mismatch causes > errors.As to fail silently, with no compile-time detection. > Analysis and Solution Approach > > The core issue is that Go's error interface allows both pointer and value > types to implement error, but the dynamic type matching in errors.As requires > exact type correspondence. This creates opportunities for silent failures > when the expected and actual types don't align. > That is the reason why it is generally recommended that error types use a pointer receiver. That statically rules out mistakes like this. It is unfortunate, that crypto/aes did not follow this recommendation. > I propose a two-part mitigation strategy: > 1. Compile-time Intent Declaration > > Explicit compile-time assertions to document intended usage patterns: > > // MyValueError is intended to be used as a valuevar _ error = MyValueError{} > // MyPointerError is intended to be used as a pointervar _ error = > (*MyPointerError)(nil) > > I think if someone does this, they are aware of the problem and should just make their Error methods a pointer receiver. In my opinion, that is a much better recommendation, as it means this mistake can't happen to users of a package, even if they don't use your linter. I know that you disagree with the blanket "all error receivers should be pointers" advice, though. > 2. Static Analysis Tool > > I've developed errortype, a static analyzer that detects inconsistencies > between intended error type usage and actual usage patterns. The tool > analyzes: > > - Function return value types > - Type assertions and type switches > - errors.As target parameters > - Method receiver consistency patterns > > For clarity: I understand your linter to 1. Check that a package declaring SomeError uses that consistently - that is, either exclusively assigns SomeError or exclusively assigns *SomeError to error. It complains, if not. 2. If that usage is consistent, checks that any users of that package *also* only assign the type consistent with that. It complains, if not. 3. Checks that all type-assertions/-switches and errors.As/errors.Is calls happen consistent with that (if it is unambiguous) In particular, AIUI you do not rely on the assignment-statements above to work, right? That seems like a very useful tool. > *Example diagnostic output:* > > main.go:14:20: Target for value error "crypto/aes.KeySizeError" is a > pointer-to-pointer, use a pointer to a value instead: "var kse > aes.KeySizeError; ... errors.As(err, &kse)". (et:err) > > Request for Feedback > > I'm particularly interested in feedback on a few points: > > 1. > > *Prevalence*: Have you encountered this pointer/value ambiguity with > error types? How common do you think this class of bug is in practice? > 2. > > *Solution Approach*: What are your thoughts on using var _ error = ... > assertions > to declare an error type's intended usage? Is this a practical convention > to adopt? > 3. > > *Tooling*: The detection heuristics are based on these assertions and > usage pattern analysis. I would be grateful for any real-world testing on > your codebases to validate their effectiveness and performance. > > The tool is currently CLI-only while I refine the detection logic based on > real-world usage patterns. Integration is planned in a later phase. > References > > - *Detailed analysis*: https://blog.fillmore-labs.com/posts/errors-1/ > - *Tool repository*: https://github.com/fillmore-labs/errortype > - *Playground example*: https://go.dev/play/p/m4SEPqkZ2Zu > > I welcome any insights, particularly from those who have encountered > similar issues or have thoughts on static analysis approaches for Go error > handling patterns. > > Best regards, Oliver > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion visit > https://groups.google.com/d/msgid/golang-nuts/0AF06538-3EAB-4E89-8F9A-25B7002237B8%40fillmore-labs.com > <https://groups.google.com/d/msgid/golang-nuts/0AF06538-3EAB-4E89-8F9A-25B7002237B8%40fillmore-labs.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfH-23ZM%3DiUTND05i%3D%2BU%2BRBiWPFXh1a-weDXAPUqSj5Ogw%40mail.gmail.com.