{"record":{"id":"c7bc3a63363b7cf4","repo":"alibaba/COLA","slug":"internal-transition-source-state-s-and-target-s","errorCode":null,"errorMessage":"Internal transition source state '%s' and target state '%s' must be same.","messagePattern":"Internal transition source state '(.+?)' and target state '(.+?)' must be same\\.","errorType":"exception","errorClass":"StateMachineException","httpStatus":null,"severity":"error","filePath":"cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/TransitionImpl.java","lineNumber":121,"sourceCode":"    }\n\n    @Override\n    public boolean equals(Object anObject){\n        if(anObject instanceof Transition){\n            Transition other = (Transition)anObject;\n            if(this.event.equals(other.getEvent())\n                    && this.source.equals(other.getSource())\n                    && this.target.equals(other.getTarget())){\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public void verify() {\n        if(type== TransitionType.INTERNAL && source != target) {\n            throw new StateMachineException(String.format(\"Internal transition source state '%s' \" +\n                    \"and target state '%s' must be same.\", source, target));\n        }\n    }\n}\n","sourceCodeStart":103,"sourceCodeEnd":126,"githubUrl":"https://github.com/alibaba/COLA/blob/352e1a867538d9cd40e1b11e4028fa652fc97557/cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/TransitionImpl.java#L103-L126","documentation":"TransitionImpl.verify() enforces that an INTERNAL transition (which runs its action without changing state) has the same source and target state. verify() is invoked from transit(), so attempting to fire an internal transition whose source differs from its target throws this StateMachineException.","triggerScenarios":"Defining a transition with internal() type (or INTERNAL TransitionType) where from(s) and to(t) specify different states, then firing the event so transit() calls verify().","commonSituations":"Developers misusing internal transitions to model state changes while expecting the action to run without a state change; converting external transitions to internal without updating to().","solutions":["Make source and target identical for internal transitions: internal().from(S).to(S).event(E)","Use a normal (external/local) transition instead of internal if the state should actually change","Split the transition into separate transitions per state if it was meant to apply to multiple source states"],"exampleFix":"// before\n.internal().from(STATE_A).to(STATE_B).event(EVENT_X) // throws on verify\n\n// after\n.internal().from(STATE_A).to(STATE_A).event(EVENT_X) // or external .from(A).to(B)","handlingStrategy":"validation","validationCode":"// when building, enforce source.equals(target) for internal transitions\nif (transitionType == INTERNAL && !source.equals(target)) {\n    throw new IllegalStateException(\"internal transition must have source == target\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    sm.fireEvent(source, event, ctx);\n} catch (StateMachineException e) {\n    if (e.getMessage().contains(\"must be same\")) {\n        // fix the transition definition: internal() requires from == to\n    } else {\n        throw e;\n    }\n}","preventionTips":["For internal() transitions always set from(x).to(x)","Use external/local transitions when the state must change","Unit-test every transition definition before deploying","Avoid mechanical conversion of external transitions to internal without adjusting to()"],"tags":["statemachine","invalid-argument-value","transition"],"backgroundTag":"invalid-argument-value","analyzedSha":"352e1a867538d9cd40e1b11e4028fa652fc97557","analyzedAt":"2026-09-08T00:46:23.972Z","contentChangedAt":"2026-09-08T00:46:23.972Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}