Go / Editorial solution guide
Diagnose Nil Pointers and Typed Nil Interfaces
Identify which value is nil, restore its construction contract, and return a genuinely nil error on success.
Read-only knowledge base ยท No public comments
Recognize the symptoms
A local program reports "invalid memory address or nil pointer dereference", or a caller enters its err != nil branch even though a concrete error pointer is nil. Printed output can be misleading: a value may display like nil while its containing interface is non-nil. These are related but distinct diagnosis paths.
Understand the likely causes
- A pointer may be uninitialized, returned as absent without checking, or stored inside another value. An interface equals nil only when both its dynamic type and dynamic value are absent. Converting a nil *Problem to error gives it a dynamic type. Separately, a method on a nil pointer can be safe only if its body deliberately avoids invalid dereferencing.
1. Locate the actual dereference
Preserve the stack trace and find the first relevant application frame in a synthetic local reproduction. Identify the exact receiver, field, or pointer being accessed and trace its construction and returned error. Do not add a nil check blindly to silence the crash: an absent required dependency should usually be rejected during initialization rather than silently skipping essential work.
2. Separate pointer nilness from interface nilness
Inspect the declared type and the dynamic type crossing the interface boundary. A safe comparison demonstration is more useful than deliberately executing a nil dereference. The complete example below shows a typed nil interface and a corrected success return. Its Error method does not dereference the receiver; the example therefore assigns no fabricated panic output to invalid code.
package main
import "fmt"
type Problem struct{}
func (*Problem) Error() string { return "problem" }
func validate(valid bool) error {
if !valid {
return &Problem{}
}
return nil
}
func main() {
var pointer *Problem
var wrapped error = pointer
fmt.Println("pointer nil", pointer == nil, "interface nil", wrapped == nil)
fmt.Println("success nil", validate(true) == nil)
}
pointer nil true interface nil false
success nil true
The converted pointer supplies a dynamic type; the explicit nil return supplies no dynamic type or value.
3. Repair construction and absence handling
For a required object, validate construction and propagate its error before storing or using it. For an optional object, document whether nil means absent and make the caller or method explicitly implement that meaning. Return nil directly from an error-returning function when no error occurred. Do not return a typed nil pointer merely because its concrete comparison is true.
4. Check dynamic types without triggering another panic
When a documented interface can hold several alternatives, use a comma-ok assertion or type switch. A direct assertion can panic on a valid different implementation. Avoid a generic reflection-based โis nilโ utility as the first repair: it can conceal an unclear API and cannot decide whether absence is valid for the domain. Prefer a precise result contract or concrete optional type.
5. Test the declared interface boundary
Write tests assigning successful and failing results to their actual interface type. Require err == nil on success and a documented category on failure. If a nil receiver is supported, test that method explicitly; otherwise reject it before invoking the method. Keep error messages human-facing and use errors.Is or errors.As for documented machine decisions.
Verify the fix
The complete example must print pointer nil true interface nil false, then success nil true. Rerun the minimized original scenario with the repaired construction or return path. Verify the intended result and absence policy, not merely survival without panic. Add tests for a missing required dependency, a supported optional absence, and a typed nil crossing the declared interface.
Keep it from recurring
- Keep constructor failures explicit, return genuine nil errors on success, and document nil-receiver support. Prefer narrow interfaces and test through them. Review value-to-interface conversions whenever a supposedly successful operation becomes a non-nil error; do not infer interface equality from formatting.