dotnet/aspnetcore · error

Unknown passkey operation '${this.attrs.operation}'.

Error message

Unknown passkey operation '${this.attrs.operation}'.

What it means

Thrown by obtainCredential when this.attrs.operation is neither 'Create' nor 'Request'. The operation is read once in connectedCallback from the element's 'operation' attribute (PasskeySubmit.razor.js:46); a missing, misspelled, or differently-cased value falls through to the else branch.

Source

Thrown at src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/Shared/PasskeySubmit.razor.js:77

    }

    disconnectedCallback() {
        this.abortController?.abort();
    }

    async obtainCredential(useConditionalMediation, signal) {
        if (!browserSupportsPasskeys) {
            throw new Error('Some passkey features are missing. Please update your browser.');
        }

        if (this.attrs.operation === 'Create') {
            return await createCredential(signal);
        } else if (this.attrs.operation === 'Request') {
            const email = new FormData(this.internals.form).get(this.attrs.emailName);
            const mediation = useConditionalMediation ? 'conditional' : undefined;
            return await requestCredential(email, mediation, signal);
        } else {
            throw new Error(`Unknown passkey operation '${this.attrs.operation}'.`);
        }
    }

    async obtainAndSubmitCredential(useConditionalMediation = false) {
        this.abortController?.abort();
        this.abortController = new AbortController();
        const signal = this.abortController.signal;
        const formData = new FormData();
        try {
            const credential = await this.obtainCredential(useConditionalMediation, signal);
            const credentialJson = JSON.stringify(credential);
            formData.append(`${this.attrs.name}.CredentialJson`, credentialJson);
        } catch (error) {
            if (error.name === 'AbortError') {
                // The user explicitly canceled the operation - return without error.
                return;
            }
            console.error(error);

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Set operation="Create" or operation="Request" exactly (case-sensitive) on every <passkey-submit> usage.
  2. If the attribute may change dynamically, re-read it in obtainCredential rather than caching in connectedCallback.
  3. Add a server-side/component assertion that the operation attribute is one of the two allowed values.
  4. Default to a known operation when the attribute is absent if that fits your UX.

Example fix

// before
<passkey-submit name="..." /> <!-- missing operation -->

// after
<passkey-submit operation="Create" name="..." />
Defensive patterns

Strategy: validation

Validate before calling

const VALID_OPS = new Set(['Create','Request']);
function validOperation(op) { return VALID_OPS.has(op); }

Type guard

type PasskeyOp = 'Create' | 'Request';
function isPasskeyOp(v: any): v is PasskeyOp { return v === 'Create' || v === 'Request'; }

Try / catch

null

Prevention

When it happens

Trigger: Rendering <passkey-submit> without an operation attribute; using operation="create" (lowercase) or operation="login"/"register"; a typo like operation="Reqest"; dynamically setting the attribute after connectedCallback (it is only read on connect).

Common situations: Template authoring mistake; casing mismatch (the code is case-sensitive); changing the attribute post-mount without re-reading; copy of the component that drops the attribute.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/10423f02b4be9450. Report an issue: GitHub.