{"record":{"id":"241e1feff02bb14c","repo":"slackhq/nebula","slug":"w-v","errorCode":null,"errorMessage":"%w: %v","messagePattern":"%w: %v","errorType":"exception","errorClass":"ErrNoCredential","httpStatus":null,"severity":"error","filePath":"handshake/machine.go","lineNumber":91,"sourceCode":"// the noise pattern and the per-message content layout. The credential for\n// `version` is fetched via getCred and used to seed the noise.HandshakeState.\n// IndexAllocator is called lazily when the first outgoing payload is built.\nfunc NewMachine(\n\tversion cert.Version,\n\tgetCred GetCredentialFunc,\n\tverifier CertVerifier,\n\tallocIndex IndexAllocator,\n\tinitiator bool,\n\tsubtype header.MessageSubType,\n) (*Machine, error) {\n\tinfo, err := subtypeInfoFor(subtype)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcred := getCred(version)\n\tif cred == nil {\n\t\treturn nil, fmt.Errorf(\"%w: %v\", ErrNoCredential, version)\n\t}\n\n\ths, err := cred.buildHandshakeState(initiator, info.pattern)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"build noise state: %w\", err)\n\t}\n\n\treturn &Machine{\n\t\ths:         hs,\n\t\tsubtype:    subtype,\n\t\tmsgs:       info.msgs,\n\t\tgetCred:    getCred,\n\t\tallocIndex: allocIndex,\n\t\tverifier:   verifier,\n\t\tmyVersion:  version,\n\t\tresult: &Result{\n\t\t\tInitiator: initiator,\n\t\t\tCipher:    cred.cipherSuite,","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/handshake/machine.go#L73-L109","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: node only has v1 certs, peer sends v2 handshake\nreturn nil, fmt.Errorf(\"%w: %v\", ErrNoCredential, version)\n// after: provision matching-version certs\nnebula-cert ca -name ca && nebula-cert sign -name host -version 2\ncertificates:\n  # load both v1 and v2 creds during migration","handlingStrategy":"type-guard","validationCode":"// before initiating, confirm a credential exists for the version\ncred := getCred(version)\nif cred == nil {\n\treturn fmt.Errorf(\"no local credential for handshake version %v; load/rotate certs\", version)\n}","typeGuard":"func hasCredential(version uint16) bool { return getCred(version) != nil }","tryCatchPattern":"m, err := handshake.NewMachine(...)\nif errors.Is(err, handshake.ErrNoCredential) {\n\t// version mismatch: reload certs or stop negotiating that version\n}","preventionTips":["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"],"tags":["handshake","certificate","version-mismatch"],"backgroundTag":"no-credential-for-version","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}