Fixing NotSupportedError and ConstraintError

These two errors are produced before the user sees anything, which makes them the easiest WebAuthn failures to diagnose and the ones most often mistaken for user behaviour. This page covers what each one means, the option defects behind them, and why neither should ever be retried. It sits under Debugging and Observability for Client-Side WebAuthn.


Signature Reference

Error Ceremony Typical cause Elapsed time
NotSupportedError either no acceptable algorithm, or an unsupported option near zero
ConstraintError either contradictory or unsatisfiable authenticator criteria near zero
NotAllowedError either user, timeout, or policy — see the separate page variable
SecurityError either RP ID, secure context or frame policy near zero

The elapsed-time column is the fastest triage tool you have. Anything rejecting in single-digit milliseconds was refused by option validation, not by a person.

Two errors, one meaning: fix your options Both are thrown before any prompt appears, and both are deploy-time bugs rather than user events. Neither is a user error, and neither should be retried NotSupportedError no acceptable algorithm, or an option the client cannot honour ConstraintError the options are internally contradictory or unsatisfiable common thread the ceremony never reached the user correct response alert engineering; never retry

Root Cause Analysis

1. An empty or unusable algorithm list. The most common cause of NotSupportedError during registration. An empty pubKeyCredParams is not “accept anything” — it specifies no acceptable algorithm, which nothing can satisfy. A list containing only algorithms your target authenticators do not implement has the same effect.

2. Requiring a capability the device lacks. Requiring a resident key on an authenticator with no discoverable-credential support, or requiring user verification on a device with neither PIN nor biometric, produces ConstraintError. The options are well-formed; they are simply unsatisfiable by the hardware present.

3. Contradictory authenticator selection. Combining criteria that cannot hold together — a cross-platform attachment with a requirement only a platform authenticator satisfies, for instance — is refused before any device is consulted.

4. An extension the client will not honour. Requesting an extension a browser does not implement is usually ignored rather than fatal, but a small number of options are validated strictly, and the rejection is immediate.

5. Retrying. Both errors are deterministic functions of the options you sent. A retry with the same options fails identically, and a retry loop turns a configuration bug into a burst of identical telemetry.

Where these are produced The client validates the options before showing anything, so the rejection is immediate. Rejected before the user is involved at all options built by your server client validates before any UI rejection immediate, no prompt elapsed time a few milliseconds A near-zero elapsed time is the signature: a user cannot dismiss a prompt that was never shown.

Step-by-Step Resolution

Step 1 — capture the options, not just the error

Log a fingerprint of the request: the algorithm identifiers, the authenticator selection criteria, the attestation preference. Never the challenge. That fingerprint identifies the defect immediately.

Step 2 — check the algorithm list first

It should contain ES256, and it should contain it first. Verify the list is non-empty and that the identifiers are the numeric COSE values rather than strings.

Step 3 — relax residency and verification from require to prefer

Unless a written policy demands otherwise. Preferring lets the client fall back to something the device can do; requiring turns a device limitation into a dead end.

Step 4 — validate the options at build time

A small function that asserts the invariants — non-empty algorithms, no contradictory selection criteria — run in a unit test, catches these before deployment rather than in a user’s browser.

Step 5 — alert on the error names

Both should have a flat zero baseline. A threshold of one is correct, and it is the opposite of how NotAllowedError should be treated.

The option mistakes behind each Most occurrences trace to a small set of malformed or contradictory option values. Two lists, both fixable in your options builder NotSupportedError an empty algorithm list only exotic algorithm identifiers an unsupported attestation value an extension the client refuses ConstraintError requiring a resident key on a device that cannot requiring user verification the device lacks contradictory authenticator selection criteria an unsatisfiable combination of the above Reproducing either takes one browser test with a deliberately restricted virtual authenticator.

Verification and Testing

Reproduce each deliberately. A virtual authenticator configured without resident-key support, combined with options requiring residency, produces ConstraintError on demand. Options with an empty algorithm list produce NotSupportedError. Both take one test each and prove your handling works.

Assert that neither is retried. A test that counts ceremony attempts after a rejection catches a retry loop that would otherwise only be visible as a spike in production.

Assert the interface state after each: the page must return to a usable idle state, and the user must see a generic failure rather than the error name.


Pitfalls

1. Showing the error name to the user. Neither name means anything to them, and both indicate a problem only you can fix.

2. Treating them as capability signals. They are option defects, not device information; probing capability by attempting a ceremony and reading the failure is both slow and unreliable.

3. Catching them alongside NotAllowedError. One is a bug and the other is routine; merging them makes both unreadable.


Frequently Asked Questions

Can a user do anything about these errors?

No. Both are produced from options your server built, before the user is consulted. The correct interface behaviour is a generic failure message and a working alternative path, with the detail routed to engineering telemetry.

Why does the same options payload work on one device and not another?

Because ConstraintError depends on what the device can do. Requiring a resident key succeeds on a platform authenticator and fails on an older security key with no discoverable-credential support. That is exactly why preferring rather than requiring is the safer default.

Does an empty extensions object cause a problem?

No. Extensions are advisory in most cases and an empty object is inert. The problems come from specific extensions that a client validates strictly, which is rare and worth identifying from the options fingerprint rather than by guessing.

Should I probe capabilities before building options?

For the enrolment decision, yes — the platform-authenticator probe tells you whether to offer enrolment at all. For the option contents, no: build options that degrade well rather than options tailored to a probe result, because the probe cannot tell you everything the ceremony will encounter.

How do these interact with a mixed authenticator population?

They are the errors you meet when a single strict options payload is sent to a diverse population. The remedy is the same one that fixes most of this class: prefer rather than require, offer several algorithms, and let the client and the user resolve the rest.

Is a rising rate ever expected?

No. Unlike user-driven errors, these have no legitimate baseline. A non-zero rate means options were deployed that some population cannot satisfy, and the options fingerprint will say which.



Validating Options Before They Ship

Because both errors are deterministic functions of the options payload, they belong to the class of bug that can be eliminated entirely rather than merely handled — and the tool for that is a validator run in your test suite rather than a check in production.

Assert the algorithm list is non-empty and contains the identifier for ES256. Assert every entry is a numeric COSE identifier rather than a string, since a serialisation that turns the numbers into strings is a common and silent way to produce an unusable list. Assert that the type field on each entry is present, because an entry missing it is ignored and a list of ignored entries is an empty list.

Assert the authenticator selection criteria are internally consistent. If your builder can produce a combination that requires something a stated attachment cannot supply, the validator should reject it before a browser does. In practice this is a handful of conditionals, and it catches the case where two independent features each add a constraint and nobody noticed the interaction.

Assert the residency and verification settings are preferences unless a named policy requires otherwise. A test that fails when somebody changes preferred to required is not obstruction; it is a prompt to record why the requirement exists, which is exactly the note that will be missing when the resulting support tickets arrive.

Run the validator over the options your real endpoint produces, not over a fixture. The defects that reach production come from configuration and from feature interactions, and a validator applied to a hand-written example will agree with itself while the deployed builder produces something else entirely.

Related