netbirdio/netbird · info
peer registered again
Error message
peer registered again
What it means
Sentinel error returned by Server.ConnectStream's select loop when the stream's own context is canceled because a newer registration for the same peer ID replaced this peer in the registry (the winning Register call cancels the old peer's context). It simply terminates the superseded gRPC stream; the peer's newest connection is unaffected.
Source
Thrown at signal/server/signal.go:49
labelTypeMessage = "message"
labelTypeTimeout = "timeout"
labelTypeDisconnected = "disconnected"
labelError = "error"
labelErrorMissingId = "missing_id"
labelErrorMissingMeta = "missing_meta"
labelErrorFailedHeader = "failed_header"
labelErrorFailedRegistration = "failed_registration"
labelRegistrationStatus = "status"
labelRegistrationFound = "found"
labelRegistrationNotFound = "not_found"
sendTimeout = 10 * time.Second
)
var (
ErrPeerRegisteredAgain = errors.New("peer registered again")
)
// Server an instance of a Signal server
type Server struct {
registry *peer.Registry
proto.UnimplementedSignalExchangeServer
dispatcher *dispatcher.Dispatcher
metrics *metrics.AppMetrics
successHeader metadata.MD
sendTimeout time.Duration
}
// NewServer creates a new Signal server
func NewServer(ctx context.Context, meter metric.Meter, metricsPrefix ...string) (*Server, error) {
appMetrics, err := metrics.NewAppMetrics(meter, metricsPrefix...)
if err != nil {View on GitHub (pinned to 93e97f4bf1)
Solutions
- No server fix needed: this is the designed stream-replacement path, not a fault.
- On the client, confirm registration via the success header and reconnect on stream end.
- Eliminate duplicate agents sharing one key to avoid constant stream stealing.
Defensive patterns
Strategy: try-catch
Type guard
func isPeerRegisteredAgain(err error) bool {
return errors.Is(err, signal.ErrPeerRegisteredAgain)
} Try / catch
err := streamHandler(stream) // ConnectStream
if err != nil && errors.Is(err, signal.ErrPeerRegisteredAgain) {
// this stream was superseded by a newer one for the same peer:
// expected during reconnect, exit the handler quietly
return nil
} Prevention
- Filter this sentinel from signal-server error dashboards; it is normal stream replacement.
- Client-side, confirm registration with the success header rather than assuming the first stream stays alive.
- Design clients to reconnect when a stream ends, since a newer stream can terminate an older one at any time.
When it happens
Trigger: A peer opens a second ConnectStream while the first is still live; the newer stream's registration wins, and the older stream exits via ctx.Done() with this error.
Common situations: Agent reconnecting without waiting for the old stream teardown; duplicate client instances using the same key; expected noise during rapid reconnects.
Related errors
- peer already registered
- signal receive stream stalled
- client not started
- an earlier read of the policy table has not returned
- sync response persistence is disabled
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/913ab6973c27e304.
Report an issue: GitHub.