{"record":{"id":"5e1117226a6cccb8","repo":"slackhq/nebula","slug":"relay-hostinfo-is-no-longer-in-the-hostmap","errorCode":null,"errorMessage":"relay hostinfo is no longer in the hostmap","messagePattern":"relay hostinfo is no longer in the hostmap","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"relay_manager.go","lineNumber":247,"sourceCode":"func AddRelay(l *slog.Logger, relayHostInfo *HostInfo, hm *HostMap, vpnIp netip.Addr, remoteIdx *uint32, relayType int, state int) (uint32, error) {\n\thm.Lock()\n\tdefer hm.Unlock()\n\tfor range 32 {\n\t\tindex, err := generateIndex(l)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\t_, inRelays := hm.Relays[index]\n\t\tif !inRelays {\n\t\t\t// Avoid standing up a relay that can't be used since only the primary hostinfo\n\t\t\t// will be pointed to by the relay logic\n\t\t\t//TODO: if there was an existing primary and it had relay state, should we merge?\n\t\t\tif !hm.unlockedMakePrimary(relayHostInfo) {\n\t\t\t\t// The tunnel was torn down after the caller grabbed relayHostInfo. A relay standing\n\t\t\t\t// on an unlinked hostinfo would never carry traffic, and its Relays entry could\n\t\t\t\t// never be reclaimed since the delete-time cleanup has already run.\n\t\t\t\treturn 0, errors.New(\"relay hostinfo is no longer in the hostmap\")\n\t\t\t}\n\n\t\t\thm.Relays[index] = relayHostInfo\n\t\t\tnewRelay := Relay{\n\t\t\t\tType:       relayType,\n\t\t\t\tState:      state,\n\t\t\t\tLocalIndex: index,\n\t\t\t\tPeerAddr:   vpnIp,\n\t\t\t}\n\n\t\t\tif remoteIdx != nil {\n\t\t\t\tnewRelay.RemoteIndex = *remoteIdx\n\t\t\t}\n\t\t\trelayHostInfo.relayState.InsertRelay(vpnIp, index, &newRelay)\n\n\t\t\treturn index, nil\n\t\t}\n\t}","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/relay_manager.go#L229-L265","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the relay creation: re-establish the connection to the peer (trigger a handshake) and call AddRelay again with the fresh HostInfo.","Handle the returned error in handleCreateRelayRequest/StartRelays paths by tearing down the pending relay state instead of leaving it Requested.","Check for root causes of hostinfo eviction (keepalive/ punches dropping, NAT rebinding) if it happens repeatedly."],"exampleFix":"// before\nidx, err := rm.AddRelay(hi, vpnIp, idx, nil, RelayEstablished, false)\n// after\nidx, err := rm.AddRelay(hi, vpnIp, idx, nil, RelayEstablished, false)\nif err != nil && err.Error() == \"relay hostinfo is no longer in the hostmap\" {\n    // re-handshake with the peer and retry with the new HostInfo\n    return retryRelay(vpnIp)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"idx, err := rm.AddRelay(hi, vpnIp, idx, nil, RelayEstablished, false)\nif err != nil {\n    if err.Error() == \"relay hostinfo is no longer in the hostmap\" {\n        // hostinfo was evicted concurrently; re-handshake and retry\n        return reestablishAndRetryRelay(vpnIp)\n    }\n    return err\n}","preventionTips":["Treat AddRelay's error as 'retry with a fresh HostInfo', not fatal","Keep the HostInfo alive across relay setup by holding the connection/handshake path","Log hostmap eviction events to correlate flapping tunnels with relay failures"],"tags":["relay","hostmap","race-condition","nebula"],"backgroundTag":"relay-hostinfo-evicted","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}