slackhq/nebula · error

ErrMachineFailed

ErrMachineFailed

Error message

handshake machine has failed

What it means

ErrMachineFailed is returned by Initiate and ProcessPacket when the handshake Machine has already entered a failed state from a previous error (every failure path sets m.failed = true). Once failed, the machine is permanently unusable by design; the caller must construct a new Machine. This guards against using a machine with corrupted or inconsistent handshake state.

Source

Thrown at handshake/errors.go:12

package handshake

import "errors"

var (
	ErrInitiateOnResponder     = errors.New("initiate called on responder")
	ErrInitiateAlreadyCalled   = errors.New("initiate already called")
	ErrInitiateNotCalled       = errors.New("initiate must be called before ProcessPacket for initiators")
	ErrPacketTooShort          = errors.New("packet too short")
	ErrPublicKeyMismatch       = errors.New("public key mismatch between certificate and handshake")
	ErrIncompleteHandshake     = errors.New("handshake completed without receiving required content")
	ErrMachineFailed           = errors.New("handshake machine has failed")
	ErrUnknownSubtype          = errors.New("unknown handshake subtype")
	ErrMissingContent          = errors.New("expected handshake content but message was empty")
	ErrUnexpectedContent       = errors.New("received unexpected handshake content")
	ErrInvalidRemoteIndex      = errors.New("peer sent an invalid index in handshake payload")
	ErrIndexAllocation         = errors.New("failed to allocate local index")
	ErrNoCredential            = errors.New("no handshake credential available for cert version")
	ErrAsymmetricCipherKeys    = errors.New("noise produced only one cipher key")
	ErrMultiMessageUnsupported = errors.New("multi-message handshake patterns are not yet supported by the manager")
	ErrSubtypeMismatch         = errors.New("packet subtype does not match handshake machine subtype")
)

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Check m.Failed() before reuse and construct a new Machine via NewMachine when true
  2. Handle the original error that failed the machine (fix keys/certs/network) before retrying
  3. Never share one Machine across goroutines or handshake attempts; one machine per handshake

Example fix

// before
res, err := m.ProcessPacket(out, pkt)
if err != nil {
    res, err = m.ProcessPacket(out, pkt) // ErrMachineFailed
}

// after
res, err := m.ProcessPacket(out, pkt)
if err != nil {
    m, err = handshake.NewMachine(cs, version, getCred) // fresh machine
    res, err = m.ProcessPacket(out, pkt)
}
Defensive patterns

Strategy: type-guard

Validate before calling

if m.Failed() {
    return errors.New("handshake machine failed; create a new one")
}

Type guard

func machineUsable(m *handshake.Machine) bool {
    return m != nil && !m.Failed()
}

Try / catch

res, err := m.ProcessPacket(out, pkt)
if errors.Is(err, handshake.ErrMachineFailed) {
    // machine is dead by design: never retry on it, recreate
    m = newHandshakeMachine()
}

Prevention

When it happens

Trigger: Calling m.Initiate(out) or m.ProcessPacket(out, packet) after any earlier call on the same Machine returned an error that set m.failed (e.g. ErrPublicKeyMismatch, ErrMissingContent, ErrSubtypeMismatch).

Common situations: Reusing a Machine object across retry loops after a failed handshake; a caller ignoring the first error and continuing to feed packets into the dead machine.

Understand the failure class

Related errors


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