slackhq/nebula · error

ErrInitiateNotCalled

ErrInitiateNotCalled

Error message

initiate must be called before ProcessPacket for initiators

What it means

ErrInitiateNotCalled is returned by Machine.ProcessPacket when the machine is an initiator but its handshake state is still at message index 0, meaning Initiate() was never called. An initiator must send the first handshake message before it can process any packet.

Source

Thrown at handshake/errors.go:8

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. Call m.Initiate(payload) first and send the returned packet before processing any incoming packets.
  2. Reorder startup logic so Initiate is guaranteed to run before the read loop calls ProcessPacket.
  3. Verify the machine was constructed with the intended initiator flag.

Example fix

// before
packet := make([]byte, 100)
msg, _, err := m.ProcessPacket(nil, packet) // initiate never called
// after
initPacket, err := m.Initiate(nil)
conn.Write(initPacket)
msg, _, err := m.ProcessPacket(nil, packet)
Defensive patterns

Strategy: try-catch

Validate before calling

if m.result.Initiator && !initiated {
    return errors.New("call Initiate before ProcessPacket")
}

Type guard

func readyToProcess(m *handshake.Machine) bool {
    r := m.Result()
    return !m.Failed() && (!r.Initiator || initiatedFlag)
}

Try / catch

msg, packet, err := m.ProcessPacket(h, buf)
if errors.Is(err, handshake.ErrInitiateNotCalled) {
    // must initiate first: build and send init packet before processing
}

Prevention

When it happens

Trigger: Calling ProcessPacket on an initiator Machine before ever calling Initiate, as in handshake/machine.go:220 (m.result.Initiator && m.hs.MessageIndex() == 0).

Common situations: Event-driven code that starts reading from the socket before sending the initiation message; wiring where incoming packets reach the machine before Initiate is invoked; tests feeding a packet to a fresh initiator.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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