QuantumNous/new-api · error · Error
Passkey verification failed
Error message
Passkey verification failed
What it means
Fallback thrown in verifyPasskey() when finishPasskeyVerification(flowToken, assertion) responds with success falsy and no message. This is the server rejecting the signed assertion at the end of the ceremony — signature verification failed, the flow token expired, or the challenge did not match. The server's message overrides the generic text when present.
Source
Thrown at web/src/features/auth/secure-verification/api.ts:167
throw new Error(i18next.t('Verification flow expired'))
}
const credential = (await navigator.credentials.get({
publicKey,
})) as PublicKeyCredential | null
if (!credential) {
throw new Error(i18next.t('Passkey verification was cancelled'))
}
const assertion = buildAssertionResult(credential)
if (!assertion) {
throw new Error(i18next.t('Unable to build Passkey assertion'))
}
const finishResponse = await finishPasskeyVerification(flowToken, assertion)
if (!finishResponse.success) {
throw new Error(
finishResponse.message || i18next.t('Passkey verification failed')
)
}
if (!finishResponse.data?.proof_token) {
throw new Error(i18next.t('Verification proof was not returned'))
}
return finishResponse.data
} catch (error: unknown) {
if (error instanceof DOMException && error.name === 'NotAllowedError') {
throw new Error(
i18next.t('Passkey verification was cancelled or timed out'),
{ cause: error }
)
}
if (error instanceof DOMException && error.name === 'InvalidStateError') {
throw new Error(
i18next.t('Passkey verification is not available in the current state'),View on GitHub (pinned to e2c7aa7b10)
Solutions
- Read the finish endpoint's response message in DevTools for the precise server-side reason.
- Verify backend RP ID and origin configuration exactly matches the site's domain and scheme.
- If running multiple backend replicas, ensure the flow/verification store (e.g. Redis) is shared, not in-memory per instance.
- Restart the flow (fresh begin) if the flow token simply expired.
Defensive patterns
Strategy: try-catch
Try / catch
try {
const proof = await verify('passkey', scope)
return proof.proof_token
} catch (e) {
const msg = getErrorMessage(e)
if (/expired|flow/i.test(msg)) {
return await verify('passkey', scope) // fresh begin/finish
}
toast.error(msg)
throw e
} Prevention
- Keep backend RP ID and origin exactly matching the served domain/scheme
- Share the flow store (Redis) across replicas so begin/finish can land on different nodes
- Restart the whole ceremony rather than reusing a flow token after delays
When it happens
Trigger: POST to the passkey finish endpoint with an assertion whose signature fails server-side verification; flow token expired between begin and finish; RP ID/origin mismatch between what the browser signed and what the backend validates; credential was deleted server-side between registration steps.
Common situations: Backend WebAuthn origin/RPID misconfigured after domain or port change; user sat on the dialog past the flow-token TTL; load-balanced backend where the flow store is not shared between begin and finish requests.
Related errors
- Failed to start verification
- Passkey verification is not supported in this environment
- Verification flow expired
- Passkey verification was cancelled
- Unable to build Passkey assertion
AI-assisted analysis of QuantumNous/new-api@e2c7aa7b10 (2026-08-15).
Data as JSON: /api/errors/59c31c4b2544b6b6.
Report an issue: GitHub.