slackhq/nebula · error
ErrInitiateOnResponder
ErrInitiateOnResponder
Error message
initiate called on responder
What it means
ErrInitiateOnResponder is returned by handshake.Machine.Initiate when the machine was created as a responder (not an initiator). Only the initiating peer may call Initiate; a responder machine cannot produce an initiation packet.
Source
Thrown at handshake/errors.go:6
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
- Ensure only the initiating side constructs its Machine with isInitiator=true and calls Initiate.
- Check your connection setup so the correct peer initiates; the responder side should only call ProcessPacket.
- Fix tests to create the machine with the initiator flag set to true when testing Initiate.
Example fix
// before m := handshake.NewMachine(cs, false) // responder packet, err := m.Initiate(nil) // after m := handshake.NewMachine(cs, true) // initiator packet, err := m.Initiate(nil)
Defensive patterns
Strategy: type-guard
Validate before calling
if !m.result.Initiator {
return errors.New("refusing to call Initiate on responder machine")
} Type guard
func canInitiate(m *handshake.Machine) bool {
return m.Result().Initiator && !m.Failed()
} Try / catch
packet, err := m.Initiate(payload)
if errors.Is(err, handshake.ErrInitiateOnResponder) {
// wrong role: switch to ProcessPacket-driven flow or rebuild as initiator
} Prevention
- Track peer roles explicitly (initiator vs responder) in connection setup
- Only call Initiate on machines constructed with isInitiator=true
- Add unit tests asserting role before handshake initiation
When it happens
Trigger: Calling Machine.Initiate on a Machine constructed as a responder (initiator=false); test scaffolding creating a responder machine then attempting Initiate, as in machine_test.go:63.
Common situations: Misconfiguring which side initiates a connection (e.g. both peers configured as responders, or calling Initiate on the wrong end in tests or connection code); reusing a responder machine to start a new handshake.
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/b7edb9e8f0c5df6f.
Report an issue: GitHub.