netbirdio/netbird · warning
already waiting for peer to come online
Error message
already waiting for peer to come online
What it means
PeersStateSubscription tracks one in-flight wait per peer in its waitingPeers map; WaitToBeOnlineAndSubscribe returns this error when a wait for the same peerID is already pending, instead of double-subscribing. The one-subscription-per-peer behavior is by design, so the error signals a duplicate wait rather than a broken state.
Source
Thrown at shared/relay/client/peer_subscription.go:86
for _, peerID := range peersID {
if _, ok := s.listenForOfflinePeers[peerID]; ok {
relevantPeers = append(relevantPeers, peerID)
}
}
s.mu.Unlock()
if len(relevantPeers) > 0 {
s.offlineCallback(relevantPeers)
}
}
// WaitToBeOnlineAndSubscribe waits for a specific peer to come online and subscribes to its state changes.
func (s *PeersStateSubscription) WaitToBeOnlineAndSubscribe(ctx context.Context, peerID messages.PeerID) error {
// Check if already waiting for this peer
s.mu.Lock()
if _, exists := s.waitingPeers[peerID]; exists {
s.mu.Unlock()
return errors.New("already waiting for peer to come online")
}
// Create a channel to wait for the peer to come online
waitCh := make(chan struct{}, 1)
s.waitingPeers[peerID] = waitCh
s.listenForOfflinePeers[peerID] = struct{}{}
s.mu.Unlock()
if err := s.subscribeStateChange(peerID); err != nil {
s.log.Errorf("failed to subscribe to peer state: %s", err)
s.mu.Lock()
if ch, exists := s.waitingPeers[peerID]; exists && ch == waitCh {
close(waitCh)
delete(s.waitingPeers, peerID)
delete(s.listenForOfflinePeers, peerID)
}
s.mu.Unlock()
return errView on GitHub (pinned to 93e97f4bf1)
Solutions
- Track outstanding waits per peer and skip or join the existing one
- Cancel the first wait (its context) and let it clean up before starting another
- Treat the error as benign idempotence feedback rather than a failure
Defensive patterns
Strategy: try-catch
Validate before calling
if inflight, ok := myWaits[peerID]; ok {
<-inflight.done // join the existing wait instead of starting a second one
return nil
}
err := subscription.WaitToBeOnlineAndSubscribe(ctx, peerID) Try / catch
if err := sub.WaitToBeOnlineAndSubscribe(ctx, peerID); err != nil {
if err.Error() == "already waiting for peer to come online" {
return nil // benign: a wait is already active for this peer
}
return err
} Prevention
- Keep your own per-peer wait registry so calls are issued once
- Cancel the previous wait's context before retrying
- Serialize connection attempts per peer with a mutex or single worker
When it happens
Trigger: Two goroutines calling WaitToBeOnlineAndSubscribe for the same offline peer (e.g. racing connection attempts), or a retry loop starting a new wait before the previous one completed and cleaned up its entry.
Common situations: Concurrent dials to the same peer in tests or custom clients; reconnect storms re-issuing waits; missing cancellation of the first wait's context.
Related errors
- invalid token data
- invalid token data: insufficient length
- invalid payload: insufficient length
- invalid signature
- invalid server IP
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/87e6bac563191cef.
Report an issue: GitHub.