{"record":{"id":"38391f0a33f82beb","repo":"netbirdio/netbird","slug":"peer-already-registered","errorCode":null,"errorMessage":"peer already registered","messagePattern":"peer already registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"signal/peer/peer.go","lineNumber":17,"sourceCode":"package peer\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"errors\"\n\n\tlog \"github.com/sirupsen/logrus\"\n\n\t\"github.com/netbirdio/netbird/shared/signal/proto\"\n\t\"github.com/netbirdio/netbird/signal/metrics\"\n)\n\nvar (\n\tErrPeerAlreadyRegistered = errors.New(\"peer already registered\")\n)\n\n// Peer representation of a connected Peer\ntype Peer struct {\n\t// a unique id of the Peer (e.g. sha256 fingerprint of the Wireguard public key)\n\tId string\n\n\tStreamID int64\n\n\t// a gRpc connection stream to the Peer\n\tStream proto.SignalExchange_ConnectStreamServer\n\t// sendMu serializes writes to Stream. gRPC forbids concurrent SendMsg on\n\t// the same ServerStream, and a peer can be the target of many senders at\n\t// once.\n\tsendMu sync.Mutex\n\n\t// registration time\n\tRegisteredAt time.Time","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/signal/peer/peer.go#L1-L35","documentation":"Sentinel error from the signal server's peer registry. Registry.Register uses sync.Map LoadOrStore: if a Peer with the same ID already exists and the incoming stream's StreamID is NOT greater than the stored one, the registration is rejected with this error so a stale stream cannot evict a live one. A strictly newer StreamID instead replaces and cancels the old peer.","triggerScenarios":"A peer reconnects to the signal server while its previous stream is still registered and the new stream carries an equal or older StreamID; two concurrent ConnectStream calls for the same peer fingerprint (sha256 of the WireGuard public key).","commonSituations":"Reconnect storms after network flaps where the old stream has not been deregistered yet; accidentally running two agents with the same key; test harnesses opening parallel streams with the same peer ID.","solutions":["Let the previous stream close and deregistration finish before reconnecting.","On the agent side, treat it as transient: the built-in reconnect loop eventually registers with a higher StreamID.","Ensure only one agent process runs per WireGuard key."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isPeerAlreadyRegistered(err error) bool {\n    return errors.Is(err, peer.ErrPeerAlreadyRegistered)\n}","tryCatchPattern":"if err := registry.Register(p); err != nil {\n    if errors.Is(err, peer.ErrPeerAlreadyRegistered) {\n        // stale or duplicate stream: do not fail the server; the newer\n        // registration wins and the client reconnect loop will retry\n        log.Debugf(\"stale registration rejected: %v\", err)\n        return nil\n    }\n    return err\n}","preventionTips":["Treat this sentinel as an expected reconnect race, not a server fault; never alert on it at high severity.","Close previous streams before opening a new one for the same peer ID.","Run only one agent per WireGuard key to avoid permanent stream-stealing."],"tags":["signal","grpc","reconnect","peer-registry","sentinel-error"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}