slackhq/nebula · error

relay hostinfo is no longer in the hostmap

Error message

relay hostinfo is no longer in the hostmap

What it means

relayManager.AddRelay links a relay HostInfo into the hostmap under a new local index. unlockedMakePrimary fails if the HostInfo's tunnel was torn down between the caller grabbing it and this call — the hostinfo node is no longer linked into the hostmap. In that case the relay can never carry traffic, so AddRelay returns this error instead of registering a dangling relay.

Source

Thrown at relay_manager.go:247

func AddRelay(l *slog.Logger, relayHostInfo *HostInfo, hm *HostMap, vpnIp netip.Addr, remoteIdx *uint32, relayType int, state int) (uint32, error) {
	hm.Lock()
	defer hm.Unlock()
	for range 32 {
		index, err := generateIndex(l)
		if err != nil {
			return 0, err
		}

		_, inRelays := hm.Relays[index]
		if !inRelays {
			// Avoid standing up a relay that can't be used since only the primary hostinfo
			// will be pointed to by the relay logic
			//TODO: if there was an existing primary and it had relay state, should we merge?
			if !hm.unlockedMakePrimary(relayHostInfo) {
				// The tunnel was torn down after the caller grabbed relayHostInfo. A relay standing
				// on an unlinked hostinfo would never carry traffic, and its Relays entry could
				// never be reclaimed since the delete-time cleanup has already run.
				return 0, errors.New("relay hostinfo is no longer in the hostmap")
			}

			hm.Relays[index] = relayHostInfo
			newRelay := Relay{
				Type:       relayType,
				State:      state,
				LocalIndex: index,
				PeerAddr:   vpnIp,
			}

			if remoteIdx != nil {
				newRelay.RemoteIndex = *remoteIdx
			}
			relayHostInfo.relayState.InsertRelay(vpnIp, index, &newRelay)

			return index, nil
		}
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Retry the relay creation: re-establish the connection to the peer (trigger a handshake) and call AddRelay again with the fresh HostInfo.
  2. Handle the returned error in handleCreateRelayRequest/StartRelays paths by tearing down the pending relay state instead of leaving it Requested.
  3. Check for root causes of hostinfo eviction (keepalive/ punches dropping, NAT rebinding) if it happens repeatedly.

Example fix

// before
idx, err := rm.AddRelay(hi, vpnIp, idx, nil, RelayEstablished, false)
// after
idx, err := rm.AddRelay(hi, vpnIp, idx, nil, RelayEstablished, false)
if err != nil && err.Error() == "relay hostinfo is no longer in the hostmap" {
    // re-handshake with the peer and retry with the new HostInfo
    return retryRelay(vpnIp)
}
Defensive patterns

Strategy: try-catch

Try / catch

idx, err := rm.AddRelay(hi, vpnIp, idx, nil, RelayEstablished, false)
if err != nil {
    if err.Error() == "relay hostinfo is no longer in the hostmap" {
        // hostinfo was evicted concurrently; re-handshake and retry
        return reestablishAndRetryRelay(vpnIp)
    }
    return err
}

Prevention

When it happens

Trigger: Calling AddRelay (directly, via migrateRelayUsed, StartRelays, or handleCreateRelayRequest) with a HostInfo whose hostmap entry was deleted concurrently — e.g. the peer connection dropped or the hostmap entry expired while the relay handshake was in flight.

Common situations: Flapping lighthouse/tunnel connections during relay establishment; handshake timeouts racing relay setup; restarts where the remote hostinfo was evicted; re-using a stale *HostInfo pointer from before a reconnect.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/5e1117226a6cccb8. Report an issue: GitHub.