{"record":{"id":"0423a0a0db89b801","repo":"gravitational/teleport","slug":"credential-not-found","errorCode":null,"errorMessage":"credential not found","messagePattern":"credential not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/auth/touchid/api.go","lineNumber":51,"sourceCode":"\t\"slices\"\n\t\"sort\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/fxamacker/cbor/v2\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/protocol/webauthncose\"\n\t\"github.com/gravitational/trace\"\n\n\t\"github.com/gravitational/teleport\"\n\twantypes \"github.com/gravitational/teleport/lib/auth/webauthntypes\"\n\t\"github.com/gravitational/teleport/lib/darwin\"\n\tlogutils \"github.com/gravitational/teleport/lib/utils/log\"\n)\n\nvar (\n\tErrCredentialNotFound = errors.New(\"credential not found\")\n\tErrNotAvailable       = errors.New(\"touch ID not available\")\n\n\t// PromptPlatformMessage is the message shown before Touch ID prompts.\n\tPromptPlatformMessage = \"Using platform authenticator, follow the OS prompt\"\n\t// PromptWriter is the writer used for prompt messages.\n\tPromptWriter io.Writer = os.Stderr\n\n\tlogger = logutils.NewPackageLogger(teleport.ComponentKey, \"TouchID\")\n)\n\nfunc promptPlatform() {\n\tif PromptPlatformMessage != \"\" {\n\t\tfmt.Fprintln(PromptWriter, PromptPlatformMessage)\n\t}\n}\n\n// AuthContext is an optional, shared authentication context.\n// Allows reusing a single authentication prompt/gesture between different","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/auth/touchid/api.go#L33-L69","documentation":"Touch ID credential lookup returns ErrCredentialNotFound when the platform authenticator has no matching credential for the requested user/relying party — e.g. enumerateCredentials returns zero results or pickCredential finds nothing. It is a sentinel error so callers (like tsh) can fall back to another MFA method.","triggerScenarios":"Calling Login for a user with no registered Touch ID credential, pickCredential when no credential matches the RP/user, or DeleteCredential/DeleteNonInteractive/TestRegister_rollback targeting a nonexistent credential ID.","commonSituations":"Touch ID credentials deleted at the OS level (System Settings or keychain reset); switching macOS user accounts; running tsh mfa/tsh login before ever registering Touch ID; credential list wiped by macOS updates.","solutions":["Re-register the Touch ID credential (e.g. `tsh mfa add`) for the user.","Check errors.Is(err, touchid.ErrCredentialNotFound) and fall back to another MFA device or prompt the user to enroll.","Enumerate available credentials first (list credential infos) and handle the zero-length case explicitly rather than assuming a credential exists."],"exampleFix":"// before\nresp, err := touchid.Login(origin, req, user)\nif err != nil {\n    return trace.Wrap(err)\n}\n// after\nresp, err := touchid.Login(origin, req, user)\nif errors.Is(err, touchid.ErrCredentialNotFound) {\n    return fallbackToOtherMFAMethod(ctx) // e.g. prompt for WebAuthn/OTP\n}\nif err != nil {\n    return trace.Wrap(err)\n}","handlingStrategy":"type-guard","validationCode":"infos, err := touchid.ListCredentials()\nif err != nil || len(infos) == 0 { return errors.New(\"no Touch ID credentials registered\") }","typeGuard":"if errors.Is(err, touchid.ErrCredentialNotFound) { /* no matching Touch ID credential */ }","tryCatchPattern":"resp, err := touchid.Login(origin, req, user)\nif errors.Is(err, touchid.ErrCredentialNotFound) {\n    return promptOtherMFAMethod(ctx) // OTP/WebAuthn fallback\n}","preventionTips":["Always offer a non-Touch-ID MFA fallback path in CLI prompts.","Check registered MFA devices (`tsh mfa ls`) before relying on Touch ID.","Handle macOS credential resets: re-enroll Touch ID after OS upgrades or keychain wipes."],"tags":["touchid","mfa","webauthn","macos"],"backgroundTag":"credential-not-found","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}