slackhq/nebula · warning

hostmap LocalIndex '%v' does not match RelayState LocalIndex

Error message

hostmap LocalIndex '%v' does not match RelayState LocalIndex

What it means

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.

Source

Thrown at ssh.go:975

				}

				s := ""
				switch r.State {
				case Requested:
					s = "requested"
				case Established:
					s = "established"
				default:
					s = "unknown"
				}

				rf.LocalIndex = r.LocalIndex
				rf.RemoteIndex = r.RemoteIndex
				rf.PeerAddr = r.PeerAddr
				rf.Type = t
				rf.State = s
				if rf.LocalIndex != k {
					rf.Error = fmt.Errorf("hostmap LocalIndex '%v' does not match RelayState LocalIndex", k)
				}
			}
			relayedHI := ifce.hostMap.QueryVpnAddr(vpnAddr)
			if relayedHI != nil {
				rf.RelayedThrough = append(rf.RelayedThrough, relayedHI.relayState.CopyRelayIps()...)
			}

			ro.RelayForAddrs = append(ro.RelayForAddrs, rf)
		}
	}
	err := enc.Encode(co)
	if err != nil {
		return err
	}
	return nil
}

func sshPrintTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Run the relays command again after a short interval; transient mismatches often resolve once indexes settle
  2. Force the affected relay to re-establish (restart the relay host or the peer) so both indexes are rebuilt
  3. Restart the node to clear the inconsistent hostmap/RelayState state
  4. Collect `relay`/hostmap debug output and report if it persists, as it indicates a real index bookkeeping bug
Defensive patterns

Strategy: retry

Validate before calling

// Before trusting a relay report, compare hostmap indexes:
// iterate relay entries and skip any where rf.Error != nil, re-querying after a settle delay

Try / catch

rf, err := queryRelays(ctx)
if err != nil {
	return err
}
for _, r := range rf {
	if r.Error != nil {
		// re-query after backoff; escalate if persistent
		continue
	}
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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