dotnet/aspnetcore · error · Error

Unknown passkey 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 component is parameterized by an operation attribute set from the Razor host; an unrecognized value indicates a configuration or integration bug — not a runtime user error.

Solutions

  1. Inspect the rendered PasskeySubmit element's data attributes to see the actual operation value being passed.
  2. Ensure the Razor component sets operation to exactly 'Create' or 'Request' (case-sensitive).
  3. If you extended the protocol with a new operation, update the if/else chain in obtainCredential to handle it before the throw.
  4. Add a type-guard or assertion at component initialization so misconfiguration surfaces at render, not on submit.

Example fix

// before
} else {
  throw new Error(`Unknown passkey operation '${this.attrs.operation}'.`);
}

// after — fail fast at init with an allowlist
const ALLOWED_OPS = new Set(['Create', 'Request']);
if (!ALLOWED_OPS.has(this.attrs.operation)) {
  throw new Error(`Invalid operation '${this.attrs.operation}'. Expected one of: ${[...ALLOWED_OPS].join(', ')}`);
}
Defensive patterns

Strategy: validation

Validate before calling

const PASSKEY_OPERATIONS = new Set(['Create', 'Request']);
function isValidOperation(op: unknown): op is 'Create' | 'Request' {
  return typeof op === 'string' && PASSKEY_OPERATIONS.has(op);
}
// assert at component attach:
if (!isValidOperation(this.attrs.operation)) {
  throw new TypeError(`operation must be 'Create' or 'Request', got ${this.attrs.operation}`);
}

Type guard

function isPasskeyOperation(v: unknown): v is 'Create' | 'Request' {
  return v === 'Create' || v === 'Request';
}

Try / catch

try {
  await obtainCredential(...);
} catch (e) {
  if (/Unknown passkey operation/.test(e.message)) {
    console.error('Component misconfigured — operation attr:', this.attrs.operation);
  } else throw e;
}

Prevention

When it happens

Trigger: The PasskeySubmit razor component passes an operation value that is not exactly 'Create' or 'Request' (case-sensitive). This happens if the attribute is left unset (undefined), mis-cased ('create'), or set to a new operation that the JS does not yet handle.

Common situations: Customizing the Blazor account components and renaming the operation enum; upgrading the template where a new operation was added server-side but the JS file was not updated; copy-paste of the component without wiring the operation attribute.

Related errors


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

Appendix: 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 3600ca084e)