{"record":{"id":"9665bf97ecc7a27b","repo":"QuantumNous/new-api","slug":"passkey-verification-was-cancelled-or-timed-out","errorCode":null,"errorMessage":"Passkey verification was cancelled or timed out","messagePattern":"Passkey verification was cancelled or timed out","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"web/src/features/auth/secure-verification/api.ts","lineNumber":178,"sourceCode":"    const assertion = buildAssertionResult(credential)\n    if (!assertion) {\n      throw new Error(i18next.t('Unable to build Passkey assertion'))\n    }\n\n    const finishResponse = await finishPasskeyVerification(flowToken, assertion)\n    if (!finishResponse.success) {\n      throw new Error(\n        finishResponse.message || i18next.t('Passkey verification failed')\n      )\n    }\n\n    if (!finishResponse.data?.proof_token) {\n      throw new Error(i18next.t('Verification proof was not returned'))\n    }\n    return finishResponse.data\n  } catch (error: unknown) {\n    if (error instanceof DOMException && error.name === 'NotAllowedError') {\n      throw new Error(\n        i18next.t('Passkey verification was cancelled or timed out'),\n        { cause: error }\n      )\n    }\n    if (error instanceof DOMException && error.name === 'InvalidStateError') {\n      throw new Error(\n        i18next.t('Passkey verification is not available in the current state'),\n        { cause: error }\n      )\n    }\n    throw error\n  }\n}\n","sourceCodeStart":160,"sourceCodeEnd":192,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/auth/secure-verification/api.ts#L160-L192","documentation":"Thrown from the catch in verifyPasskey() when navigator.credentials.get() rejects with a DOMException named NotAllowedError. Per the WebAuthn spec this is the standard signal that the user declined the browser prompt or the operation timed out waiting for user consent. The original exception is preserved via { cause: error } for diagnostics.","triggerScenarios":"User clicks 'Cancel' on the browser/OS passkey prompt; user does not interact with the prompt before the browser's timeout (commonly ~30-120s); user denies the subsequent biometric/OS authentication step.","commonSituations":"Everyday user cancellation during security-sensitive operations (delete token, change password); user walks away from the machine; touch/face ID fails repeatedly then aborts.","solutions":["No system fix: this is expected user behaviour — route the user back to method selection (e.g. offer the 2FA code).","Show a neutral 'cancelled' message rather than an error-styled toast.","If it fires without any prompt appearing, check that only one credentials.get() runs at a time (concurrent calls are aborted with NotAllowedError)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isUserCancellation = (e: unknown): boolean =>\n  (e instanceof Error && /cancelled/i.test(e.message)) ||\n  (e instanceof DOMException && e.name === 'NotAllowedError')","tryCatchPattern":"try {\n  await verify('passkey', scope)\n} catch (e) {\n  if (isUserCancellation(e)) {\n    returnToMethodSelection() // silent, no error toast\n  }\n  toast.error(getErrorMessage(e))\n}","preventionTips":["Classify NotAllowedError as cancellation and never show it as a failure","Offer the 2FA fallback after cancellation so users are not stuck","Avoid launching two credentials.get() ceremonies concurrently"],"tags":["passkey","webauthn","user-cancellation","dom-exception"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}