slackhq/nebula · error
ErrLocalIndexCollision
ErrLocalIndexCollision
Error message
local index collision
What it means
ErrLocalIndexCollision is returned by CheckAndComplete when the local index carried by the completed handshake is already registered in the main or pending index map but belongs to a DIFFERENT hostinfo. The manager cannot install the new hostinfo without overwriting a live connection, so it rejects the completion.
Source
Thrown at handshake_manager.go:416
doTrigger = hm.lightHouse.addCalculatedRemotes(vpnAddr)
}
if doTrigger {
select {
case hm.trigger <- vpnAddr:
default:
}
}
hm.Unlock()
hm.lightHouse.QueryServer(vpnAddr)
return hostinfo
}
var (
ErrExistingHostInfo = errors.New("existing hostinfo")
ErrAlreadySeen = errors.New("already seen")
ErrLocalIndexCollision = errors.New("local index collision")
)
// CheckAndComplete checks for any conflicts in the main and pending hostmap
// before adding hostinfo to main. If err is nil, it was added. Otherwise err will be:
//
// ErrAlreadySeen if we already have an entry in the hostmap that has seen the
// exact same handshake packet
//
// ErrExistingHostInfo if we already have an entry in the hostmap for this
// VpnIp and the new handshake was older than the one we currently have
//
// ErrLocalIndexCollision if we already have an entry in the main or pending
// hostmap for the hostinfo.localIndexId.
func (hm *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket uint8, f *Interface) (*HostInfo, error) {
hm.mainHostMap.Lock()
defer hm.mainHostMap.Unlock()
hm.Lock()
defer hm.Unlock()View on GitHub (pinned to dd8f660c0a)
Solutions
- Retry the handshake; the manager allocates a new index on the next attempt
- If frequent, check for version skew between peers echoing stale index values
- Inspect hostmap index churn (many handshakes/second) which increases collision probability; increase index space or reduce reconnect storms
Defensive patterns
Strategy: retry
Try / catch
hi, err := hm.CheckAndComplete(hostinfo)
if errors.Is(err, ErrLocalIndexCollision) {
// drop this completion; initiator will re-handshake with a fresh index
return
} Prevention
- Ensure DeleteHostInfo is invoked on teardown so indices recycle
- Minimize handshake storms (backoff reconnect timers)
- Keep peers on the same version to avoid stale index echoes
When it happens
Trigger: Two different remote hosts (or two hostinfo entries for different VPN addresses) complete handshakes that resolve to the same localIndexId — main index map hit at handshake_manager.go:457 or pending index map hit at :463 with a differing hostinfo.
Common situations: Extreme index-map churn exhausting/exhaustedly recycling the index space; bugs or version skew causing peers to echo stale indices; long-lived connections plus rapid reconnects from many peers behind one NAT.
Related errors
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/81d22b1787eba3d4.
Report an issue: GitHub.