{"record":{"id":"81d22b1787eba3d4","repo":"slackhq/nebula","slug":"errlocalindexcollision","errorCode":"ErrLocalIndexCollision","errorMessage":"local index collision","messagePattern":"local index collision","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"handshake_manager.go","lineNumber":416,"sourceCode":"\t\tdoTrigger = hm.lightHouse.addCalculatedRemotes(vpnAddr)\n\t}\n\n\tif doTrigger {\n\t\tselect {\n\t\tcase hm.trigger <- vpnAddr:\n\t\tdefault:\n\t\t}\n\t}\n\n\thm.Unlock()\n\thm.lightHouse.QueryServer(vpnAddr)\n\treturn hostinfo\n}\n\nvar (\n\tErrExistingHostInfo    = errors.New(\"existing hostinfo\")\n\tErrAlreadySeen         = errors.New(\"already seen\")\n\tErrLocalIndexCollision = errors.New(\"local index collision\")\n)\n\n// CheckAndComplete checks for any conflicts in the main and pending hostmap\n// before adding hostinfo to main. If err is nil, it was added. Otherwise err will be:\n//\n// ErrAlreadySeen if we already have an entry in the hostmap that has seen the\n// exact same handshake packet\n//\n// ErrExistingHostInfo if we already have an entry in the hostmap for this\n// VpnIp and the new handshake was older than the one we currently have\n//\n// ErrLocalIndexCollision if we already have an entry in the main or pending\n// hostmap for the hostinfo.localIndexId.\nfunc (hm *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket uint8, f *Interface) (*HostInfo, error) {\n\thm.mainHostMap.Lock()\n\tdefer hm.mainHostMap.Unlock()\n\thm.Lock()\n\tdefer hm.Unlock()","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/handshake_manager.go#L398-L434","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"hi, err := hm.CheckAndComplete(hostinfo)\nif errors.Is(err, ErrLocalIndexCollision) {\n    // drop this completion; initiator will re-handshake with a fresh index\n    return\n}","preventionTips":["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"],"tags":["handshake","hostmap","index-collision"],"backgroundTag":"handshake-index-collision","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"}