nats-io/nats-server · warning

gsl: nil channel

Error message

gsl: nil channel

What it means

ErrNilChan ('gsl: nil channel') is returned when registering a notification/interest watcher with a nil notification channel. registerNotification rejects a nil notify argument (server/sublist.go:174), and the identical sentinel also exists in the server's own sublist package (server/sublist.go:45) with the same message.

Source

Thrown at server/gsl/gsl.go:42

// interested subscribers. Subscribers can have wildcard subjects to
// match multiple published subjects.

// Common byte variables for wildcards and token separator.
const (
	pwc     = '*'
	pwcs    = "*"
	fwc     = '>'
	fwcs    = ">"
	tsep    = "."
	btsep   = '.'
	_EMPTY_ = ""
)

// Sublist related errors
var (
	ErrInvalidSubject    = errors.New("gsl: invalid subject")
	ErrNotFound          = errors.New("gsl: no matches found")
	ErrNilChan           = errors.New("gsl: nil channel")
	ErrAlreadyRegistered = errors.New("gsl: notification already registered")
)

// SimpleSublist is an alias type for GenericSublist that takes
// empty values, useful for tracking interest only without any
// unnecessary allocations.
type SimpleSublist = GenericSublist[struct{}]

// NewSimpleSublist will create a simple sublist.
func NewSimpleSublist() *SimpleSublist {
	return &GenericSublist[struct{}]{root: newLevel[struct{}]()}
}

// A GenericSublist stores and efficiently retrieves subscriptions.
type GenericSublist[T comparable] struct {
	sync.RWMutex
	root  *level[T]
	count uint32

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Create the notification channel (make(chan interface{}, size)) before registering the notification.
  2. Check for nil before calling registerNotification and return a clearer caller-side error.
  3. Unregister the old notification before nil-ing/replacing the channel rather than registering with a nil channel.

Example fix

// before
sl.Notify(n) // n.Chan is nil -> gsl: nil channel
// after
n.Chan = make(chan interface{}, 16)
if err := sl.Notify(n); err != nil {
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

if n == nil || n.Chan == nil {
    return errors.New("notification channel must be non-nil before registration")
}

Try / catch

if err := sl.Notify(n); err != nil && errors.Is(err, gsl.ErrNilChan) {
    return fmt.Errorf("create the notification channel first: %w", err)
}

Prevention

When it happens

Trigger: Calling NewSublistNotification / notification registration APIs passing nil for the notification channel, e.g. a watcher created without a result channel or after the channel was closed and set to nil.

Common situations: Code paths where a results channel is lazily created but registration happens first, watch/interest tracking in tests passing nil, cleanup code nil-ing channels before unregistering notifications.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/c1bd34aeb67c33a7. Report an issue: GitHub.