{"record":{"id":"d3e15a2ade47435c","repo":"gravitational/teleport","slug":"device-already-holds-a-registered-credential","errorCode":null,"errorMessage":"device already holds a registered credential","messagePattern":"device already holds a registered credential","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"lib/auth/webauthncli/fido2.go","lineNumber":66,"sourceCode":"\t// goroutine.\n\tfido2DeviceMaxWait = 100 * time.Millisecond\n\n\t// Timeout for blocking operations.\n\t// Functions fail with FIDO_ERR_RX on timeout.\n\tfido2DeviceTimeout = 30 * time.Second\n\n\t// Operation retry interval.\n\t// Keep it less frequent than 5Hz / 0.2s.\n\tfido2RetryInterval = 500 * time.Millisecond\n\n\t// Timeout for touch.Status operations.\n\t// Keep it less frequent than 5Hz / 0.2s.\n\tfido2TouchMaxWait = 200 * time.Millisecond\n)\n\n// User-friendly device filter errors.\nvar (\n\terrHasExcludedCredential = errors.New(\"device already holds a registered credential\")\n\terrNoPasswordless        = errors.New(\"device not registered for passwordless\")\n\terrNoPlatform            = errors.New(\"device cannot fulfill platform attachment requirement\")\n\terrNoRK                  = errors.New(\"device lacks resident key capabilities\")\n\terrNoUV                  = errors.New(\"device lacks PIN or user verification capabilities necessary to support passwordless\")\n\terrPasswordlessU2F       = errors.New(\"U2F devices cannot do passwordless\")\n)\n\n// TouchRequest abstracts *libfido2.TouchRequest for testing.\ntype TouchRequest interface {\n\tStatus(timeout time.Duration) (touched bool, err error)\n\tStop() error\n}\n\n// FIDODevice abstracts *libfido2.Device for testing.\ntype FIDODevice interface {\n\t// Info mirrors libfido2.Device.Info.\n\tInfo() (*libfido2.DeviceInfo, error)\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/auth/webauthncli/fido2.go#L48-L84","documentation":"errHasExcludedCredential is a user-friendly device filter error in the FIDO2 device filter. When during a registration the device already holds a credential listed in the excludeCredentials list (or libfido2 reports ErrUserPresenceRequired, which YubiKey4 uses to signal an existing credential), the device is filtered out with this error instead of attempting a duplicate registration.","triggerScenarios":"fido2.go:537 — libfido2.ErrUserPresenceRequired observed by a YubiKey4 when the credential already exists; fido2.go:548 — the device filter detects the device contains a credential from the excludeList during Register.","commonSituations":"Re-registering a security key that already holds this site's credential (common on YubiKey4, which stores few credentials); running tsh mfa add twice with the same key; idempotent registration attempts.","solutions":["Use the existing credential — the key is already registered, so just log in instead of registering again.","Delete the old registration first (tsh mfa rm <name>) and then re-register the key.","If you must create a new credential, wipe/reset the YubiKey (with caution: factory reset erases all credentials).","If intentional double registration is desired, drop the existing credential from the excludeCredentials list."],"exampleFix":"// before\ncred, err := wancli.Register(ctx, origin, creation, prompt, nil)\n// after\nif errors.Is(err, errHasExcludedCredential) {\n\treturn nil, trace.AlreadyExists(\"device already registered; remove old MFA device first\")\n}\ncred, err := wancli.Register(ctx, origin, creation, prompt, nil)","handlingStrategy":"type-guard","validationCode":"// before registering, list existing MFA devices and skip registration if the key is present\ntsh mfa ls","typeGuard":"func isExcludedCredentialErr(err error) bool {\n\treturn strings.Contains(err.Error(), \"already holds a registered credential\") ||\n\t\terrors.Is(err, libfido2.ErrUserPresenceRequired)\n}","tryCatchPattern":"_, err := wancli.Register(ctx, origin, cc, prompt, nil)\nif isExcludedCredentialErr(err) {\n\treturn trace.AlreadyExists(\"device already registered; use it to log in or remove the old device\")\n}","preventionTips":["Check existing registrations before re-registering a key.","Remember YubiKey4 signals existing credentials via ErrUserPresenceRequired.","Avoid including the same key twice in excludeCredentials-based flows.","Use tsh mfa rm to delete stale registrations instead of forcing duplicate credentials."],"tags":["fido2","yubikey","webauthn","duplicate-registration"],"backgroundTag":"authenticator-credential-already-exists","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}