Go

Why does this function return a non-nil error even when nothing failed? How do you fix it?

Question 80HardGo 1.22 to 1.25
type MyErr struct{ Msg string }

func (e *MyErr) Error() string { return e.Msg }

func validate(ok bool) error {
    var e *MyErr
    if !ok {
        e = &MyErr{"invalid"}
    }
    return e // BUG
}

func main() {
    if err := validate(true); err != nil {
        fmt.Println("failed:", err) // runs and prints "failed: <nil>"
    }
}

return e converts a nil *MyErr to error. The result has type word *MyErr and data word nil, so it is not equal to nil. The caller sees a failure. fmt recovers the panic from a nil-receiver Error() and prints <nil>, but any direct call such as err.Error() dereferences the nil pointer (e.Msg) and panics.

Fix: return the error interface directly and write a literal nil on the success path. Do not declare error results with concrete pointer types.

func validate(ok bool) error {
    if !ok {
        return &MyErr{"invalid"}
    }
    return nil
}

The same trap appears when a function returns *MyErr and the caller assigns the result to an error variable. The idiom is that exported functions return error, not concrete error types. staticcheck's SA4023 flags this pattern: a comparison of an interface with nil whose result can never change.

More on Interfaces, Methods & Embedding

All 35 Interfaces, Methods & Embedding questions