Golang / Golang Internals and Memory Management Interview Questions
What is the nil interface pitfall in Go and how do you avoid it?
One of Go's most confusing bugs: a nil pointer of a concrete type, when assigned to an interface, produces a non-nil interface value. This breaks code that checks if err != nil — the check passes even though the underlying value is nil.
type MyError struct{ code int } func (e *MyError) Error() string { return fmt.Sprintf("error %d", e.code) } // Bug: returns a non-nil interface holding a nil *MyError func riskyOp(fail bool) error { var err *MyError // nil *MyError pointer if fail { err = &MyError{code: 42} } return err // WRONG: even when fail=false, the returned error is NOT nil // The interface has {type=*MyError, data=nil} } e := riskyOp(false) if e != nil { fmt.Println("ERROR:", e) // This PRINTS â interface is non-nil! } // Fix 1: return untyped nil when there is no error func safeOp(fail bool) error { if fail { return &MyError{code: 42} } return nil // nil interface â both type and data are nil } // Fix 2: use a concrete return type if the caller always knows the type func safeOp2(fail bool) *MyError { if fail { return &MyError{code: 42} } return nil // now nil really means nil } // Detect with reflect if debugging: fmt.Println(e == nil) // false (interface non-nil) fmt.Println(reflect.ValueOf(e).IsNil()) // true (data pointer is nil)
The rule: never return a typed nil as an interface return type. If your function returns an interface (like error), always return the bare nil keyword on the success path, not a nil pointer of a concrete type. The errors.Is and errors.As functions from Go 1.13+ are also affected — they work correctly because they unwrap the interface, but the initial != nil check still fails.
More Related questions...