Introduction

In Go, an interface value is nil only when both its type and value are nil. If you assign a typed nil pointer (like (*MyStruct)(nil)) to an interface, the interface is NOT nil because it has a concrete type. This causes if iface == nil checks to fail unexpectedly.

This is one of the most confusing aspects of Go's type system and leads to subtle bugs where nil checks pass through to nil pointer dereferences.

Symptoms

  • Function returns what appears to be nil but err == nil evaluates to false
  • fmt.Println(err) shows <nil> but err == nil is false
  • nil check on interface returns false even though the underlying pointer is nil

Common Causes

  • Returning a typed nil pointer as an interface type: return (*MyError)(nil)
  • Assigning nil pointer to interface variable: var err error = (*CustomError)(nil)
  • Function returns concrete nil type where interface is expected

Step-by-Step Fix

  1. 1.Understand the typed nil interface trap: See why typed nil is not interface nil.
  2. 2.```go
  3. 3.type MyError struct{}

func (e *MyError) Error() string { return "error" }

func returnsError() error { var err *MyError = nil return err // Returns (type=*MyError, value=nil) - NOT a nil interface! }

func main() { err := returnsError() fmt.Println(err == nil) // false! Interface has type *MyError fmt.Printf("%T\n", err) // *main.MyError fmt.Printf("%v\n", err) // <nil> } ```

  1. 1.Return bare nil for interface types: Use nil directly, not a typed nil.
  2. 2.```go
  3. 3.// BAD: returns typed nil
  4. 4.func parse(input string) error {
  5. 5.var err *ParseError
  6. 6.if input == "" {
  7. 7.return err // (*ParseError)(nil) - NOT nil interface
  8. 8.}
  9. 9.// ...
  10. 10.return nil // This is fine - bare nil
  11. 11.}

// GOOD: return bare nil func parse(input string) error { if input == "" { return nil // Bare nil is a nil interface } if invalid { return &ParseError{Input: input} } return nil } ```

  1. 1.Use reflect.Value.IsNil for runtime interface nil checks: When you must check at runtime.
  2. 2.```go
  3. 3.import "reflect"

func IsInterfaceNil(v interface{}) bool { if v == nil { return true } rv := reflect.ValueOf(v) switch rv.Kind() { case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface: return rv.IsNil() default: return false } } ```

  1. 1.Use static analysis to catch typed nil returns: Use nilaway or go-staticcheck.
  2. 2.```bash
  3. 3.# Install staticcheck:
  4. 4.go install honnef.co/go/tools/cmd/staticcheck@latest

# Run on your code: staticcheck ./...

# Output: # main.go:15:2: returning a typed nil pointer as an interface value (SA4023) ```

Prevention

  • Always return bare nil from functions with interface return types
  • Never assign a typed nil pointer to an interface variable
  • Run staticcheck or nilaway in CI to catch typed nil interface issues
  • Document this pattern in your team's Go style guide