{"record":{"id":"d0e5a9e518a4478f","repo":"slackhq/nebula","slug":"hostmap-localindex-v-does-not-match-relaystate","errorCode":null,"errorMessage":"hostmap LocalIndex '%v' does not match RelayState LocalIndex","messagePattern":"hostmap LocalIndex '(.+?)' does not match RelayState LocalIndex","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"ssh.go","lineNumber":975,"sourceCode":"\t\t\t\t}\n\n\t\t\t\ts := \"\"\n\t\t\t\tswitch r.State {\n\t\t\t\tcase Requested:\n\t\t\t\t\ts = \"requested\"\n\t\t\t\tcase Established:\n\t\t\t\t\ts = \"established\"\n\t\t\t\tdefault:\n\t\t\t\t\ts = \"unknown\"\n\t\t\t\t}\n\n\t\t\t\trf.LocalIndex = r.LocalIndex\n\t\t\t\trf.RemoteIndex = r.RemoteIndex\n\t\t\t\trf.PeerAddr = r.PeerAddr\n\t\t\t\trf.Type = t\n\t\t\t\trf.State = s\n\t\t\t\tif rf.LocalIndex != k {\n\t\t\t\t\trf.Error = fmt.Errorf(\"hostmap LocalIndex '%v' does not match RelayState LocalIndex\", k)\n\t\t\t\t}\n\t\t\t}\n\t\t\trelayedHI := ifce.hostMap.QueryVpnAddr(vpnAddr)\n\t\t\tif relayedHI != nil {\n\t\t\t\trf.RelayedThrough = append(rf.RelayedThrough, relayedHI.relayState.CopyRelayIps()...)\n\t\t\t}\n\n\t\t\tro.RelayForAddrs = append(ro.RelayForAddrs, rf)\n\t\t}\n\t}\n\terr := enc.Encode(co)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc sshPrintTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {","sourceCodeStart":957,"sourceCodeEnd":993,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/ssh.go#L957-L993","documentation":"sshPrintRelays builds a relay report from the hostmap and RelayState tables and flags an entry when the hostmap's LocalIndex for a relay differs from the RelayState key it was found under. The two indexes must agree for a relay to be considered consistent; a mismatch means internal index corruption or a stale/partially torn-down relay entry. The error is attached to the reported relay field rather than aborting the command.","triggerScenarios":"Querying relays over the SSH debug interface while a hostmap relay entry's LocalIndex no longer matches the key of its corresponding relayState entry — typically after index churn, relay re-establishment, or a torn-down relay that was only partially removed.","commonSituations":"Inspecting `relays` via nebula's SSH debug console on a busy mesh where relays have flapped; debugging stale relay state after a peer reconnected and reallocated indexes.","solutions":["Run the relays command again after a short interval; transient mismatches often resolve once indexes settle","Force the affected relay to re-establish (restart the relay host or the peer) so both indexes are rebuilt","Restart the node to clear the inconsistent hostmap/RelayState state","Collect `relay`/hostmap debug output and report if it persists, as it indicates a real index bookkeeping bug"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Before trusting a relay report, compare hostmap indexes:\n// iterate relay entries and skip any where rf.Error != nil, re-querying after a settle delay","typeGuard":null,"tryCatchPattern":"rf, err := queryRelays(ctx)\nif err != nil {\n\treturn err\n}\nfor _, r := range rf {\n\tif r.Error != nil {\n\t\t// re-query after backoff; escalate if persistent\n\t\tcontinue\n\t}\n}","preventionTips":["Re-run relay inspection after index churn instead of acting on a single snapshot","Restart relay hosts cleanly rather than letting relays flap","Report persistent mismatches to maintainers — they indicate hostmap/RelayState bookkeeping bugs"],"tags":["relay","hostmap","state-inconsistency","nebula"],"backgroundTag":"relay-index-mismatch","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"}