
Why Does My nil Interface Not Equal nil? (Go's Most Famous Gotcha)
An interface value is a pair: (dynamic type, dynamic value). It equals nil only when both parts are nil. A nil *T stored in an error (or any interface) still has a type: *T. The pair is (*T, nil), which is not a nil interface. err != nil is true. fmt.Println(err) prints <nil>. That is the bug.
The failure
This function "succeeds." The caller thinks it failed.
package main
import "fmt"
type MyError struct{ msg string }
func (e *MyError) Error() string { return e.msg }
func mightFail() error {
var err *MyError // nil pointer
return err // boxed as error: type=*MyError, value=nil
}
func main() {
err := mightFail()
fmt.Println(err) // <nil>
fmt.Println(err == nil) // false
if err != nil {
fmt.Println("treating success as failure")
}
}
The same trap with io.Writer:
func writeAll(w io.Writer, p []byte) {
if w != nil {
w.Write(p) // panics if w holds a nil *bytes.Buffer
}
}
func main() {
var buf *bytes.Buffer // nil
writeAll(buf, []byte("x"))
}
var w io.Writer is a real nil interface. writeAll(w, p) is safe. Passing the nil *bytes.Buffer is not: the interface is non-nil, Write is called, the receiver is nil.
Why this happens

var e error → (type=nil, value=nil) e == nil → true
var p *MyError = nil
e = p → (type=*MyError, value=nil) e == nil → false
return nil → (type=nil, value=nil)
Assignment into an interface always fills the type slot from the static type of the value. return err where err is *MyError copies that type in, even when the pointer is nil.
fmt prints <nil> because the value is nil. Your if err != nil looks at the interface, which still has a type.
Method calls on a typed-nil interface dispatch. The panic, if any, is inside the method when it dereferences the nil receiver, not at if w != nil.
The fix
Return untyped nil when there is no error. Keep the result variable as error, not *MyError.
func mightFail() error {
return nil
}
func mightFail2() (err error) {
var concrete *MyError
if bad {
concrete = &MyError{msg: "nope"}
return concrete
}
return nil // not return concrete
}
If you must inspect a boxed pointer, assert it:
if err != nil {
if e, ok := err.(*MyError); ok && e == nil {
// typed nil: treat as success, or don't produce this value
}
}
Do not produce that value. return nil on the interface type is the rule. Same for any interface you return: if the concrete pointer is nil, do not assign it into the interface.
Learn more: