{"record":{"id":"e84102e76096156b","repo":"dotnet/aspnetcore","slug":"unknown-action-action","errorCode":null,"errorMessage":"Unknown action: ${action}","messagePattern":"Unknown action: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Identity/samples/IdentitySample.PasskeyUI/wwwroot/app.js","lineNumber":87,"sourceCode":"            abortController?.abort();\n            abortController = new AbortController();\n            return await navigator.credentials.get({\n                publicKey: options,\n                mediation: useConditionalMediation ? 'conditional' : undefined,\n                signal: abortController.signal,\n            });\n        }\n\n        async function fetchAndSubmitCredential(action, useConditionalMediation = false) {\n            try {\n                const username = new FormData(form).get('username');\n                let credential;\n                if (action === 'register') {\n                    credential = await fetchNewCredential(username);\n                } else if (action === 'authenticate') {\n                    credential = await fetchExistingCredential(username, useConditionalMediation);\n                } else {\n                    throw new Error('Unknown action: ' + action);\n                }\n                var credentialJson = JSON.stringify(credential);\n                form.addEventListener('formdata', (e) => {\n                    e.formData.append('action', action);\n                    e.formData.append('credential', credentialJson);\n                }, { once: true });\n                form.submit();\n            } catch (error) {\n                // Ignore abort errors, they are expected when the user cancels the operation.\n                if (error.name !== 'AbortError') {\n                    statusMessage.textContent = 'Error: ' + error.message;\n                    throw error;\n                }\n            }\n        }\n\n        form.addEventListener('submit', (e) => {\n            if (e.submitter?.name == 'action') {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Identity/samples/IdentitySample.PasskeyUI/wwwroot/app.js#L69-L105","documentation":"Thrown by fetchAndSubmitCredential() when the action parameter is neither 'register' nor 'authenticate'. This is a defensive assertion guarding the two known submit flows; any other value indicates a logic error in how the form's submitter value is wired up.","triggerScenarios":"The submit handler reads e.submitter.value and passes it as action; if a button has name='action' but a value other than 'register'/'authenticate', or if fetchAndSubmitCredential is called programmatically with a typo, this branch fires. Also triggered during conditional mediation auto-auth (line 112) if the literal changes.","commonSituations":"Adding a third action button (e.g., 'delete credential') without extending the if/else chain; renaming submit button values in markup without updating JS; copy-paste error invoking fetchAndSubmitCredential with wrong argument.","solutions":["Check the submit button markup — every button with name='action' must have value 'register' or 'authenticate'.","If adding a new action, extend the if/else-if chain in fetchAndSubmitCredential to handle it.","Pass only known action literals when calling fetchAndSubmitCredential programmatically."],"exampleFix":"// before\nif (action === 'register') { /*...*/ }\nelse if (action === 'authenticate') { /*...*/ }\nelse { throw new Error('Unknown action: ' + action); }\n\n// after (explicit allow-list)\nconst KNOWN_ACTIONS = new Set(['register', 'authenticate']);\nif (!KNOWN_ACTIONS.has(action)) {\n    throw new Error(`Unknown action: ${action}`);\n}","handlingStrategy":"validation","validationCode":"const KNOWN_ACTIONS = new Set(['register', 'authenticate']);\nfunction dispatch(action, useCond = false) {\n  if (!KNOWN_ACTIONS.has(action)) {\n    console.error(`Unknown action: ${action}`);\n    return;\n  }\n  return fetchAndSubmitCredential(action, useCond);\n}","typeGuard":"function isKnownAction(a) { return a === 'register' || a === 'authenticate'; }","tryCatchPattern":null,"preventionTips":["Centralize the allowed action set in one constant and reference it from markup and JS.","Name submit buttons name='action' with values only from the known set.","Add a unit check that any programmatic call to fetchAndSubmitCredential uses a known literal."],"tags":["webauthn","passkey","assertion","control-flow","identity-sample"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}