QuantumNous/new-api · error · Error
Unable to build Passkey assertion
Error message
Unable to build Passkey assertion
What it means
Thrown in verifyPasskey() when buildAssertionResult(credential) returns a falsy value after a successful navigator.credentials.get(). The helper converts the PublicKeyCredential into the JSON-serializable assertion the finish endpoint expects; null means the credential's response was missing required fields (clientDataJSON, authenticatorData, signature) or had an unexpected shape.
Source
Thrown at web/src/features/auth/secure-verification/api.ts:162
const publicKey = prepareCredentialRequestOptions(
beginResponse.data?.options ?? beginResponse.data
)
const flowToken = beginResponse.data?.flow_token
if (!flowToken) {
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 }View on GitHub (pinned to e2c7aa7b10)
Solutions
- Log the credential object (id, type, response keys) to see which field is absent.
- If using a mock in tests, make it include response.clientDataJSON, authenticatorData, signature (and userHandle when present), base64url-encoded.
- Try a different authenticator/device to rule out the specific security key.
- Harden buildAssertionResult to report which field was missing instead of returning null.
Defensive patterns
Strategy: type-guard
Type guard
const isUsableAssertion = (a: unknown): boolean =>
typeof a === 'object' && a !== null &&
typeof (a as { id?: unknown }).id === 'string' &&
typeof (a as { response?: { signature?: unknown } }).response?.signature === 'string' Try / catch
try {
await verify('passkey', scope)
} catch (e) {
if (/assertion/i.test(getErrorMessage(e))) {
promptUserToRetryWithDifferentAuthenticator()
}
throw e
} Prevention
- When mocking navigator.credentials in tests, include clientDataJSON, authenticatorData and signature
- Harden buildAssertionResult to name the missing field instead of returning null
- Retry with another device/security key when the authenticator emits malformed responses
When it happens
Trigger: The authenticator returns a credential whose response lacks signature or authenticatorData; the browser hands back a credential object whose response is an unexpected subtype; a polyfill or testing library returning a minimal fake credential.
Common situations: Testing with mocked navigator.credentials that omits response fields; exotic authenticators or security keys with non-standard attestation responses; browser extension interfering with the credentials API.
Related errors
- Invalid Passkey response
- Passkey verification is not supported in this environment
- Failed to start verification
- Verification flow expired
- Passkey verification was cancelled
AI-assisted analysis of QuantumNous/new-api@e2c7aa7b10 (2026-08-15).
Data as JSON: /api/errors/c8dc8bb994ebd710.
Report an issue: GitHub.