slackhq/nebula · error · ErrNoCredential

%w: %v

Error message

%w: %v

What it means

NewMachine in the handshake package wraps ErrNoCredential with the negotiated Noise protocol version when no matching credential exists for that version. Nebula keeps separate credentials/certificates per certificate (pki) version; if the peer proposes a version for which the local node holds no credential, the handshake machine cannot be built.

Source

Thrown at handshake/machine.go:91

// the noise pattern and the per-message content layout. The credential for
// `version` is fetched via getCred and used to seed the noise.HandshakeState.
// IndexAllocator is called lazily when the first outgoing payload is built.
func NewMachine(
	version cert.Version,
	getCred GetCredentialFunc,
	verifier CertVerifier,
	allocIndex IndexAllocator,
	initiator bool,
	subtype header.MessageSubType,
) (*Machine, error) {
	info, err := subtypeInfoFor(subtype)
	if err != nil {
		return nil, err
	}

	cred := getCred(version)
	if cred == nil {
		return nil, fmt.Errorf("%w: %v", ErrNoCredential, version)
	}

	hs, err := cred.buildHandshakeState(initiator, info.pattern)
	if err != nil {
		return nil, fmt.Errorf("build noise state: %w", err)
	}

	return &Machine{
		hs:         hs,
		subtype:    subtype,
		msgs:       info.msgs,
		getCred:    getCred,
		allocIndex: allocIndex,
		verifier:   verifier,
		myVersion:  version,
		result: &Result{
			Initiator: initiator,
			Cipher:    cred.cipherSuite,

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Load/rotate credentials for the version the peers are negotiating (issue new certs at the matching version)
  2. Upgrade/downgrade Nebula so both sides support the same certificate version
  3. Check `nebula-cert` output and the pki files in the config to confirm which versions are available
  4. Verify lighthouse/host map entries are not pinned to peers on incompatible cert versions

Example fix

// before: node only has v1 certs, peer sends v2 handshake
return nil, fmt.Errorf("%w: %v", ErrNoCredential, version)
// after: provision matching-version certs
nebula-cert ca -name ca && nebula-cert sign -name host -version 2
certificates:
  # load both v1 and v2 creds during migration
Defensive patterns

Strategy: type-guard

Validate before calling

// before initiating, confirm a credential exists for the version
cred := getCred(version)
if cred == nil {
	return fmt.Errorf("no local credential for handshake version %v; load/rotate certs", version)
}

Type guard

func hasCredential(version uint16) bool { return getCred(version) != nil }

Try / catch

m, err := handshake.NewMachine(...)
if errors.Is(err, handshake.ErrNoCredential) {
	// version mismatch: reload certs or stop negotiating that version
}

Prevention

When it happens

Trigger: Calling NewMachine (directly or via beginHandshake during a connection attempt) where getCred(version) returns nil — i.e. the incoming handshake's version string does not match any loaded local credential (cert + key) for the current certificate version.

Common situations: Peer upgraded to a newer certificate format/version while this node still holds only old-version certs (or vice versa), stale certificates not yet rotated, mixed-version Nebula cluster during a pki version migration.

Related errors


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