slackhq/nebula · error

ErrMissingContent

ErrMissingContent

Error message

expected handshake content but message was empty

What it means

ErrMissingContent is returned by processPayload when a decrypted handshake message was empty but the pattern flags for that message expect a payload and/or certificate. The peer sent nothing where the protocol required content, so the handshake cannot proceed and the machine is marked failed.

Source

Thrown at handshake/errors.go:14

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. Have the peer attach its payload and certificate to the message the pattern expects
  2. Retry the handshake from scratch in case of a transient truncation
  3. Verify both peers run the same handshake pattern definition

Example fix

// before: peer writes final message without content
buf := m.hs.WriteMessage()
conn.Write(buf)

// after
buf := m.hs.WriteMessage(nil)
buf = handshake.AppendPayload(buf, payload)
buf = handshake.AppendCert(buf, cert)
conn.Write(buf)
Defensive patterns

Strategy: try-catch

Validate before calling

if len(msg) == 0 && patternExpectsContent(stage) {
    return fmt.Errorf("peer sent empty handshake message at stage %d", stage)
}

Type guard

func msgHasRequiredContent(msg []byte, flags handshake.MsgFlags) bool {
    return len(msg) > 0 || !(flags.ExpectsPayload || flags.ExpectsCert)
}

Try / catch

res, err := m.ProcessPacket(out, pkt)
if errors.Is(err, handshake.ErrMissingContent) {
    // peer must re-send with payload/cert attached; restart handshake
    return restartHandshake(conn)
}

Prevention

When it happens

Trigger: processPayload receives nil/empty message bytes while msgFlags{expectsPayload: true, expectsCert: true} (handshake/machine.go:289); exercised in machine_test.go:188.

Common situations: Peer sends an empty final handshake frame after a failure on its side; a truncated transmission; peer version that does not attach cert/payload to the expected message.

Understand the failure class

Related errors


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