{"record":{"id":"98a89e676525c1a1","repo":"ethereum/go-ethereum","slug":"can-t-create-subscription-after-subscribe-call-has","errorCode":null,"errorMessage":"can't create subscription after subscribe call has returned","messagePattern":"can't create subscription after subscribe call has returned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rpc/subscription.go","lineNumber":124,"sourceCode":"\tmu           sync.Mutex\n\tsub          *Subscription\n\tbuffer       []any\n\tcallReturned bool\n\tactivated    bool\n}\n\n// CreateSubscription returns a new subscription that is coupled to the\n// RPC connection. By default subscriptions are inactive and notifications\n// are dropped until the subscription is marked as active. This is done\n// by the RPC server after the subscription ID is send to the client.\nfunc (n *Notifier) CreateSubscription() *Subscription {\n\tn.mu.Lock()\n\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)","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/ethereum/go-ethereum/blob/6bb0588ad8e7f922e4ad5580f51265a4097af08f/rpc/subscription.go#L106-L142","documentation":"Notifier.CreateSubscription panics when called after the subscribe RPC call has already returned (n.callReturned is set by takeSubscription when the server finishes handling the method). The subscription must be created synchronously inside the service method, before it returns, so the server can send the subscription ID back to the client.","triggerScenarios":"Capturing the *Notifier in a closure or storing it on the service struct, then calling CreateSubscription from a background goroutine after the subscribe method returned; calling CreateSubscription from inside notifier.Notify's send path or a later request.","commonSituations":"Starting a goroutine in the subscribe method that lazily creates the subscription; keeping the Notifier for a later handshake; refactoring a handler so the subscription creation moved outside the request scope.","solutions":["Move CreateSubscription into the service method body, before it returns.","If creation depends on async work, do the work first (or synchronously) and then create the subscription before returning.","Do not store or reuse a Notifier across requests; request a fresh one via api.Notifier(...) in each call."],"exampleFix":"// before\nfunc (s *API) Subscribe(r *http.Request) (*rpc.Subscription, error) {\n    notifier, _ := s.api.Notifier(r.Context())\n    go func() { s.sub = notifier.CreateSubscription() }() // runs after return -> panic\n    ...\n}\n\n// after\nfunc (s *API) Subscribe(r *http.Request) (*rpc.Subscription, error) {\n    notifier, _ := s.api.Notifier(r.Context())\n    sub := notifier.CreateSubscription() // inside the call\n    go func() { /* only notify here */ }()\n    return sub, nil\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep CreateSubscription inside the synchronous body of the subscribe method.","Never capture a Notifier in goroutines or store it for later use; obtain a fresh Notifier per request.","Start background work only after the subscription is created and the method is about to return."],"tags":["rpc","server","subscription","lifecycle","goroutine","api-misuse","panic","go"],"backgroundTag":null,"analyzedSha":"6bb0588ad8e7f922e4ad5580f51265a4097af08f","analyzedAt":"2026-08-15T10:06:53.996Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}