sipeed/picoclaw · error

events: handler is nil

Error message

events: handler is nil

What it means

Returned by EventChannel.Subscribe and EventChannel.SubscribeOnce (channel.go:55,72) when the handler argument is nil. The channel-based variant SubscribeChan takes no handler and is the intended API when you want events on a <-chan Event instead of a callback.

Source

Thrown at pkg/events/subscription.go:19

package events

import (
	"context"
	"errors"
	"log"
	"runtime/debug"
	"sync"
	"sync/atomic"
	"time"
)

const defaultSubscriberBuffer = 16

var (
	// ErrBusClosed is returned when subscribing to a closed event bus.
	ErrBusClosed = errors.New("events: bus is closed")
	// ErrNilHandler is returned when subscribing without a handler.
	ErrNilHandler = errors.New("events: handler is nil")
)

// Handler processes a runtime event delivered to a subscription.
type Handler func(context.Context, Event) error

// SubscribeOptions controls how a subscription receives events.
type SubscribeOptions struct {
	Name         string
	Buffer       int
	Priority     int
	Concurrency  ConcurrencyKind
	Backpressure BackpressurePolicy
	// Timeout bounds how long the subscription worker waits for one handler call.
	// Handlers should still honor ctx cancellation; timed-out calls keep running
	// until their handler returns.
	Timeout     time.Duration
	PanicPolicy PanicPolicy
}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Pass a non-nil events.Handler callback to Subscribe/SubscribeOnce
  2. If you want events on a channel, call SubscribeChan(ctx, opts) which returns (Subscription, <-chan Event, error) and requires no handler
  3. Guard before subscribing: if handler == nil { return errors.New("handler required") } at the call site

Example fix

// before
var h events.Handler // nil
sub, err := ch.Subscribe(ctx, opts, h)

// after
sub, eventsCh, err := ch.SubscribeChan(ctx, opts) // channel-based, no handler
// or: h := func(ctx context.Context, e events.Event) error { return nil }
Defensive patterns

Strategy: validation

Validate before calling

if handler == nil {
    return fmt.Errorf("events handler required for Subscribe")
}
sub, err := ch.Subscribe(ctx, opts, handler)

Try / catch

sub, err := ch.Subscribe(ctx, opts, handler)
if err != nil && errors.Is(err, events.ErrNilHandler) {
    // programmer error: fix the call site; consider falling back to SubscribeChan
}

Prevention

When it happens

Trigger: Passing a nil events.Handler (e.g. a func variable that was never assigned, or a conditional handler that evaluated to nil) to Subscribe or SubscribeOnce.

Common situations: Handler selected by a map/switch that misses a case and leaves the variable nil; refactoring a SubscribeChan call to Subscribe and forgetting to pass the handler; tests registering a stub that is nil.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/55995402cfcecce2. Report an issue: GitHub.