{"record":{"id":"023467b47ed2e2a8","repo":"ethereum/go-ethereum","slug":"can-t-notify-before-subscription-is-created","errorCode":null,"errorMessage":"can't Notify before subscription is created","messagePattern":"can't Notify before subscription is created","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rpc/subscription.go","lineNumber":137,"sourceCode":"\tdefer n.mu.Unlock()\n\n\tif n.sub != nil {\n\t\tpanic(\"can't create multiple subscriptions with Notifier\")\n\t} else if n.callReturned {\n\t\tpanic(\"can't create subscription after subscribe call has returned\")\n\t}\n\tn.sub = &Subscription{ID: n.h.idgen(), namespace: n.namespace, err: make(chan error, 1)}\n\treturn n.sub\n}\n\n// Notify sends a notification to the client with the given data as payload.\n// If an error occurs the RPC connection is closed and the error is returned.\nfunc (n *Notifier) Notify(id ID, data any) error {\n\tn.mu.Lock()\n\tdefer n.mu.Unlock()\n\n\tif n.sub == nil {\n\t\tpanic(\"can't Notify before subscription is created\")\n\t} else if n.sub.ID != id {\n\t\tpanic(\"Notify with wrong ID\")\n\t}\n\tif n.activated {\n\t\treturn n.send(n.sub, data)\n\t}\n\tn.buffer = append(n.buffer, data)\n\treturn nil\n}\n\n// takeSubscription returns the subscription (if one has been created). No subscription can\n// be created after this call.\nfunc (n *Notifier) takeSubscription() *Subscription {\n\tn.mu.Lock()\n\tdefer n.mu.Unlock()\n\tn.callReturned = true\n\treturn n.sub\n}","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/ethereum/go-ethereum/blob/6bb0588ad8e7f922e4ad5580f51265a4097af08f/rpc/subscription.go#L119-L155","documentation":"Notifier.Notify panics when no subscription has been created on this Notifier yet. Notify is only valid for delivering data on the subscription obtained from CreateSubscription; calling it before that (or on a fresh Notifier from api.Notifier in a different call) is a programming error.","triggerScenarios":"Calling notifier.Notify(id, data) without a prior notifier.CreateSubscription() on the same Notifier instance; storing the ID from another session and notifying through a new Notifier.","commonSituations":"Event-emitting code that runs before the first subscriber calls the subscribe method; passing only the subscription ID around instead of the Subscription object; refactoring where Notify is called from a constructor before the handler created the subscription.","solutions":["Create the subscription first with CreateSubscription and only Notify with the returned sub.ID afterwards.","Gate event emission on 'at least one active subscription' state in your service.","Use the Subscription object's own channel-based delivery if you hold the *Subscription rather than the Notifier."],"exampleFix":"// before\nnotifier.Notify(subID, event) // no CreateSubscription on this notifier -> panic\n\n// after\nsub := notifier.CreateSubscription()\nnotifier.Notify(sub.ID, event)","handlingStrategy":"validation","validationCode":"// Only notify through a Notifier you created a subscription on:\nif s := notifier.Subscription(); s == nil { // conceptual guard: track creation yourself\n    return errors.New(\"no subscription yet\")\n}\nnotifier.Notify(sub.ID, data)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pair every Notifier with its Subscription in a small struct so Notify calls always have the counterpart.","Suppress event emission until the first subscribe request has created a subscription.","Never call Notify from a fresh api.Notifier(ctx) in a different request."],"tags":["rpc","server","subscription","ordering","api-misuse","panic","go"],"backgroundTag":null,"analyzedSha":"6bb0588ad8e7f922e4ad5580f51265a4097af08f","analyzedAt":"2026-08-15T10:06:53.996Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}