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
- Load/rotate credentials for the version the peers are negotiating (issue new certs at the matching version)
- Upgrade/downgrade Nebula so both sides support the same certificate version
- Check `nebula-cert` output and the pki files in the config to confirm which versions are available
- 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
- Keep certs for every version you advertise support for during migrations
- Rotate certs on all nodes together rather than letting versions drift
- Test mixed-version clusters in staging before upgrading the fleet
- Monitor logs for ErrNoCredential to catch version drift early
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.