apache/beam · error
Logger cannot be nil
Error message
Logger cannot be nil
What it means
SetLogger in the Beam Go log package panics if called with a nil Logger. The global logger must always have a valid implementation; installing nil would cause nil dereferences in every later log call, so it is rejected up front. It is intended for initialization-time use only.
Solutions
- Construct a real Logger implementation before calling SetLogger (e.g. log.NewLogger(...) or a slog/zap adapter).
- Guard with an interface-nil check plus reflect-based typed-nil check if the logger comes from an external factory.
- Handle the factory error path so a failed logger construction aborts initialization instead of returning nil.
Example fix
// before
var myLog *MyLogger
log.SetLogger(myLog) // typed nil, panics
// after
if myLog == nil { myLog = NewMyLogger() }
log.SetLogger(myLog) Defensive patterns
Strategy: type-guard
Validate before calling
if logger == nil {
return errors.New("cannot install nil logger")
} Type guard
func isNilLogger(l log.Logger) bool {
if l == nil { return true }
v := reflect.ValueOf(l)
switch v.Kind() {
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface:
return v.IsNil()
}
return false
} Try / catch
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("SetLogger rejected the logger: %v", r)
}
}() Prevention
- Handle factory errors so a failed logger construction never returns nil.
- Beware typed-nil pointers wrapped in interfaces — check the concrete value too.
- Install a default logger at program start before any other init code.
When it happens
Trigger: Calling log.SetLogger(nil), or passing a typed-nil (e.g. a nil *MyLogger wrapped in the Logger interface) which is non-nil as an interface but nil as a pointer.
Common situations: Initializing logging from a config factory that returned nil on failure; forgetting to construct the logger before registering it; the classic typed-nil-interface pitfall in Go.
Related errors
- panic(msg)
- AfterProcessingTime trigger set without a delay or…
- At least one subtrigger required for composite triggers.
- attempted to add namespace to missing coder id
- attempted to add namespace to missing windowing strategy id
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/40d1f6173c246e26.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/log/log.go:75
}
var logger atomic.Value
// concreteLogger works around atomic.Value's requirement that the type
// be identical for all callers.
type concreteLogger struct {
Logger
}
func init() {
logger.Store(&concreteLogger{&Structural{}})
}
// SetLogger sets the global Logger. Intended to be called during initialization
// only.
func SetLogger(l Logger) {
if l == nil {
panic("Logger cannot be nil")
}
logger.Store(&concreteLogger{l})
}
// Output logs the given message to the global logger. Calldepth is the count
// of the number of frames to skip when computing the file name and line number.
func Output(ctx context.Context, sev Severity, calldepth int, msg string) {
logger.Load().(Logger).Log(ctx, sev, calldepth+1, msg) // +1 for this frame
}
// User-facing logging functions.
// Debug writes the fmt.Sprint-formatted arguments to the global logger with
// debug severity.
func Debug(ctx context.Context, v ...any) {
Output(ctx, SevDebug, 1, fmt.Sprint(v...))
}
View on GitHub (pinned to 12126d8942)