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
- Have the peer attach its payload and certificate to the message the pattern expects
- Retry the handshake from scratch in case of a transient truncation
- 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
- Ensure peer attaches payload and cert to the exact messages the pattern expects
- Use length-prefixed framing to prevent message truncation
- Cross-version integration tests for handshake message layout
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/b60c0b95628003e9.
Report an issue: GitHub.