slackhq/nebula · error
tunnel message counter is exhausted
Error message
tunnel message counter is exhausted
What it means
The relay tunnel's Noise message counter (NextMessageCounter) returned !ok, meaning the 64-bit sequence space or window is exhausted, so no further encrypted relay messages can be sent on that tunnel. The packet is dropped and the error surfaces to the caller.
Source
Thrown at inside.go:459
func (f *Interface) prepareSendVia(via *HostInfo,
relay *Relay,
ad,
nb,
out []byte,
nocopy bool,
) ([]byte, error) {
if noiseutil.EncryptLockNeeded {
// NOTE: for goboring AESGCMTLS we need to lock because of the nonce check
via.ConnectionState.writeLock.Lock()
}
c, ok := via.ConnectionState.NextMessageCounter()
if !ok {
if noiseutil.EncryptLockNeeded {
via.ConnectionState.writeLock.Unlock()
}
f.dropExhausted(via, c, "Dropping outbound relay packets, tunnel message counter is exhausted")
return nil, fmt.Errorf("tunnel message counter is exhausted")
}
out = header.Encode(out, header.Version, header.Message, header.MessageRelay, relay.RemoteIndex, c)
f.connectionManager.OutNoRebind(via)
// Authenticate the header and payload, but do not encrypt for this message type.
// The payload consists of the inner, unencrypted Nebula header, as well as the end-to-end encrypted payload.
if len(out)+len(ad)+via.ConnectionState.eKey.Overhead() > cap(out) {
if noiseutil.EncryptLockNeeded {
via.ConnectionState.writeLock.Unlock()
}
via.logger(f.l).Error("SendVia out buffer not large enough for relay",
"outCap", cap(out),
"payloadLen", len(ad),
"headerLen", len(out),
"cipherOverhead", via.ConnectionState.eKey.Overhead(),
)
return nil, io.ErrShortBufferView on GitHub (pinned to dd8f660c0a)
Solutions
- Re-handshake the tunnel to create a fresh ConnectionState and reset the counter
- Reconnect the affected tunnel (let nebula renegotiate keys via boring-cryptostream or restart)
- Investigate why the counter is exhausted (should not happen at normal rates — indicates a stale/long-lived tunnel)
- Upgrade nebula if your version lacks automatic key rotation
Defensive patterns
Strategy: fallback
Try / catch
out, err := f.SendVia(via, relay, ...)
if err != nil && errors.Is(err, errCounterExhausted) {
// trigger re-handshake / tunnel renegotiation, then retry
f.connectionManager.RequestRebind(via)
} Prevention
- Monitor tunnel age and force periodic re-handshakes
- Alert on 'message counter is exhausted' drops — they indicate stale long-lived tunnels
- Keep nebula updated so key rotation resets counters
When it happens
Trigger: Calling SendVia / anonymous senders via prepareSendVia after the via connection's message counter has been incremented past its maximum — i.e. an extremely long-lived, high-volume relay tunnel.
Common situations: Long-running tunnels that never re-handshake; a stuck/stale ConnectionState whose counter was not reset; encryption lock path (EncryptLockNeeded) leaving counter consumed repeatedly.
Related errors
- unable to find host
- unable to find host with relay
- relay hostinfo is no longer in the hostmap
- failed to generate unique localIndexId
- could not find hostinfo
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/c815d08dbf86bef3.
Report an issue: GitHub.