nats-io/nats-server · warning
gsl: no matches found
Error message
gsl: no matches found
What it means
ErrNotFound ('gsl: no matches found') is returned by the sublist remove path when the value being removed does not exist in the subject tree. gsl.go:388 returns it when traversal hits a nil node (no matching path), and gsl.go:412 returns it when removeFromNode finds no exact match for the stored value.
Source
Thrown at server/gsl/gsl.go:41
// provides a facility to match subjects from published messages to
// 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]View on GitHub (pinned to 3a66a489d2)
Solutions
- Verify the exact subject and value used at insertion; remove must match both subject path and stored value.
- Treat ErrNotFound on removal as benign idempotent cleanup where possible, or track insertion state to skip redundant removes.
- Guard against double-unsubscribe in client shutdown paths (check a 'removed' flag before calling remove).
Example fix
// before
sl.Remove(subject, sub) // may return gsl: no matches found
// after
if err := sl.Remove(subject, sub); err != nil && !errors.Is(err, gsl.ErrNotFound) {
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// track whether the interest was inserted before removing
if !inserted[subject] {
return nil // nothing to remove
} Try / catch
if err := sl.Remove(subject, value); err != nil && !errors.Is(err, gsl.ErrNotFound) {
return err // real failure; NotFound is idempotent
} Prevention
- Make removal idempotent by tolerating ErrNotFound.
- Use identical subject strings (and values) for insert and remove - store them together.
- Guard against double-unsubscribe with cleanup flags.
When it happens
Trigger: Calling Remove/RemoveBatch on a subject that was never inserted, or removing with a different value than the one registered at an existing subject node (removeFromNode returns false); also removing after the interest was already deleted.
Common situations: Unsubscribe with a slightly different subject than the subscribe (trailing wildcard mismatch), double-unsubscribe in cleanup code, removal racing with an earlier removal, clients tracked in maps that were already cleaned up.
Related errors
- gsl: invalid subject
- gsl: nil channel
- account jwt not found
- auth callout violation: auth callout response is not for exp
- auth callout violation: auth callout response is not for ser
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/9f534e070932ff93.
Report an issue: GitHub.