{"record":{"id":"e609a5297f52ecb7","repo":"netbirdio/netbird","slug":"engine-not-started-e609a5","errorCode":null,"errorMessage":"engine not started","messagePattern":"engine not started","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/server/server.go","lineNumber":1635,"sourceCode":"\tctx context.Context,\n\treq *proto.GetPeerSSHHostKeyRequest,\n) (*proto.GetPeerSSHHostKeyResponse, error) {\n\tif ctx.Err() != nil {\n\t\treturn nil, ctx.Err()\n\t}\n\n\ts.mutex.Lock()\n\tconnectClient := s.connectClient\n\tstatusRecorder := s.statusRecorder\n\ts.mutex.Unlock()\n\n\tif connectClient == nil {\n\t\treturn nil, errors.New(\"client not initialized\")\n\t}\n\n\tengine := connectClient.Engine()\n\tif engine == nil {\n\t\treturn nil, errors.New(\"engine not started\")\n\t}\n\n\tpeerAddress := req.GetPeerAddress()\n\thostKey, found := engine.GetPeerSSHKey(peerAddress)\n\n\tresponse := &proto.GetPeerSSHHostKeyResponse{\n\t\tFound: found,\n\t}\n\n\tif !found {\n\t\treturn response, nil\n\t}\n\n\tresponse.SshHostKey = hostKey\n\n\tif statusRecorder == nil {\n\t\treturn response, nil\n\t}","sourceCodeStart":1617,"sourceCodeEnd":1653,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/server/server.go#L1617-L1653","documentation":"Returned by the daemon's GetPeerSSHHostKey RPC handler (client/server/server.go:1635) when the connect client exists but its Engine() returns nil. The connect client outlives the engine: during connection setup, reconnect cycles, or after the engine stopped, the client object is present while the engine is not, so peer SSH host-key lookup has no data source.","triggerScenarios":"GetPeerSSHHostKey called while the daemon is between engine start and engine ready (connecting state), during a management-triggered reconnect that nils the engine, or right after the engine was stopped but before the whole client was torn down.","commonSituations":"SSH into a peer immediately after daemon start while management login is still in flight; transient window during network-map resync or engine rebuild; UI querying SSH keys during down/logout teardown.","solutions":["Retry after the daemon reports Connected (the engine is set once the connection is established).","In scripts, add a short bounded retry loop around the SSH host-key lookup.","If it persists while Connected, check daemon logs for an engine start failure (interface creation, routing setup)."],"exampleFix":"// before: single shot during connecting window\nresp, err := daemonClient.GetPeerSSHHostKey(ctx, req)\n\n// after: bounded retry around the transient window\nvar resp *daemonpb.GetPeerSSHHostKeyResponse\nfor i := 0; i < 5; i++ {\n    resp, err = daemonClient.GetPeerSSHHostKey(ctx, req)\n    if err == nil || !strings.Contains(err.Error(), \"engine not started\") {\n        break\n    }\n    time.Sleep(500 * time.Millisecond)\n}","handlingStrategy":"validation","validationCode":"// Engine exists only once connected; check before the lookup.\nstatus, err := daemonClient.Status(ctx)\nif err != nil {\n    return err\n}\nif status.GetStatus() != daemonpb.StatusEnum_CONNECTED {\n    return fmt.Errorf(\"engine not ready (status %s); retry shortly\", status.GetStatus())\n}","typeGuard":null,"tryCatchPattern":"var resp *daemonpb.GetPeerSSHHostKeyResponse\nerr := retryOnMessage(ctx, 5, 500*time.Millisecond,\n    \"engine not started\",\n    func() (*daemonpb.GetPeerSSHHostKeyResponse, error) {\n        return daemonClient.GetPeerSSHHostKey(ctx, req)\n    })","preventionTips":["Treat 'engine not started' as a transient connecting-window state: bounded retry.","Watch SubscribeStatus events to learn when the engine is up instead of polling blindly."],"tags":["go","netbird","daemon","ssh","rpc","lifecycle","retry"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}