{"record":{"id":"9b5521231eb815ec","repo":"apereo/cas","slug":"state-stateid-eventid-transitionid-does-no","errorCode":null,"errorMessage":"State [<stateId>:<eventId>:<transitionId>] does not have a matching transition for <eventId>","messagePattern":"State \\[<stateId>:<eventId>:<transitionId>\\] does not have a matching transition for <eventId>","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"core/cas-server-core-authentication-mfa-api/src/main/java/org/apereo/cas/authentication/MultifactorAuthenticationUtils.java","lineNumber":77,"sourceCode":"     */\n    public static Event validateEventIdForMatchingTransitionInContext(final String eventId,\n                                                                      final Optional<RequestContext> context,\n                                                                      final Map<String, Object> attributes) {\n        val attributesMap = new LocalAttributeMap<>(attributes);\n        val event = new Event(eventId, eventId, attributesMap);\n        LOGGER.trace(\"Attempting to find a matching transition for event id [{}]\", event.getId());\n        return context.map(ctx -> {\n            LOGGER.trace(\"Reviewing current state [{}], event [{}] and transition [{}]\",\n                ctx.getCurrentState(), ctx.getCurrentEvent(), ctx.getCurrentTransition());\n            val def = ctx.getMatchingTransition(event.getId());\n            if (def == null) {\n                val msg = String.format(\"State [%s:%s:%s] does not have a matching transition for %s\",\n                    ctx.getCurrentState().getId(),\n                    ctx.getCurrentEvent() != null ? ctx.getCurrentEvent().getId() : \"N/A\",\n                    ctx.getCurrentTransition() != null ? ctx.getCurrentTransition().getId() : \"N/A\",\n                    event.getId());\n                LoggingUtils.error(LOGGER, msg);\n                throw new AuthenticationException(msg);\n            }\n            return event;\n        }).orElse(event);\n    }\n\n\n    /**\n     * Resolve event via multivalued attribute set.\n     *\n     * @param principal         the principal\n     * @param attributeValue    the attribute value\n     * @param registeredService the service\n     * @param service           the service\n     * @param context           the context\n     * @param provider          the provider\n     * @param predicate         the predicate\n     * @return the set\n     */","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-authentication-mfa-api/src/main/java/org/apereo/cas/authentication/MultifactorAuthenticationUtils.java#L59-L95","documentation":"MultifactorAuthenticationUtils.validateEventIdForMatchingTransitionInContext verifies that the event about to be signaled in the login webflow has a matching transition defined on the current flow state. When the current state has no transition for that event id, it logs the state/event/transition triple and throws AuthenticationException, aborting the flow rather than leaving the state machine stuck.","triggerScenarios":"Calling the utility (or the MFA webflow 'event' action that uses it) with an eventId that the current webflow state cannot transition on — e.g. posting an MFA provider event like 'mfa-gauth' when the state only defines transitions for registered/supported providers, or a stale browser retry after the flow already advanced.","commonSituations":"MFA provider id misconfigured (event never registered as a transition); user double-submits or uses browser back so the event no longer matches the current state; custom MFA provider registered without wiring its transition into the login flow; CAS upgrade changing flow state ids while a bookmarked URL replays an old event.","solutions":["Verify the MFA provider id in cas.authn.mfa.* matches the event/transition ids registered in the login webflow.","Have the user restart the login flow (fresh /login request) instead of retrying a stale page/back-button state.","If a custom provider, confirm its transition is registered via the webflow customization/execution listener so the state defines that event.","Clear the bot-stale session/flow state (expire Webflow session storage) and check for load-balanced nodes with mismatched CAS versions."],"exampleFix":"// before: posting event 'mfa-sms' but only google-authenticator is configured\n// after: align configured provider with the event\n// application.properties\ncas.authn.mfa.gauth.core.rank=0\ncas.authn.mfa.sms.enabled=true  // ensure provider and its flow transition both registered","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return MultifactorAuthenticationUtils.validateEventIdForMatchingTransitionInContext(event, ctx);\n} catch (AuthenticationException e) {\n  LOGGER.warn(\"No matching transition; restarting flow\", e);\n  return null;\n}","preventionTips":["Ensure registered MFA provider ids match webflow transition/event names","Instruct users to restart login from /login instead of refreshing/back-navigating mid-flow","Keep webflow state definitions in sync with enabled MFA providers after upgrades"],"tags":["webflow","mfa","state-machine"],"backgroundTag":"invalid-state-transition","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}