{"record":{"id":"9f534e070932ff93","repo":"nats-io/nats-server","slug":"gsl-no-matches-found","errorCode":null,"errorMessage":"gsl: no matches found","messagePattern":"gsl: no matches found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/gsl/gsl.go","lineNumber":41,"sourceCode":"// provides a facility to match subjects from published messages to\n// interested subscribers. Subscribers can have wildcard subjects to\n// match multiple published subjects.\n\n// Common byte variables for wildcards and token separator.\nconst (\n\tpwc     = '*'\n\tpwcs    = \"*\"\n\tfwc     = '>'\n\tfwcs    = \">\"\n\ttsep    = \".\"\n\tbtsep   = '.'\n\t_EMPTY_ = \"\"\n)\n\n// Sublist related errors\nvar (\n\tErrInvalidSubject    = errors.New(\"gsl: invalid subject\")\n\tErrNotFound          = errors.New(\"gsl: no matches found\")\n\tErrNilChan           = errors.New(\"gsl: nil channel\")\n\tErrAlreadyRegistered = errors.New(\"gsl: notification already registered\")\n)\n\n// SimpleSublist is an alias type for GenericSublist that takes\n// empty values, useful for tracking interest only without any\n// unnecessary allocations.\ntype SimpleSublist = GenericSublist[struct{}]\n\n// NewSimpleSublist will create a simple sublist.\nfunc NewSimpleSublist() *SimpleSublist {\n\treturn &GenericSublist[struct{}]{root: newLevel[struct{}]()}\n}\n\n// A GenericSublist stores and efficiently retrieves subscriptions.\ntype GenericSublist[T comparable] struct {\n\tsync.RWMutex\n\troot  *level[T]","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/gsl/gsl.go#L23-L59","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nsl.Remove(subject, sub) // may return gsl: no matches found\n// after\nif err := sl.Remove(subject, sub); err != nil && !errors.Is(err, gsl.ErrNotFound) {\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// track whether the interest was inserted before removing\nif !inserted[subject] {\n    return nil // nothing to remove\n}","typeGuard":null,"tryCatchPattern":"if err := sl.Remove(subject, value); err != nil && !errors.Is(err, gsl.ErrNotFound) {\n    return err // real failure; NotFound is idempotent\n}","preventionTips":["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."],"tags":["nats","sublist","not-found","unsubscribed"],"backgroundTag":"subscription-not-found","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}