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.

Reply via email to