alibaba/COLA · error · StateMachineException

Internal transition source state '%s' and target state '%s'

Error message

Internal transition source state '%s' and target state '%s' must be same.

What it means

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.

Source

Thrown at cola-components/cola-component-statemachine/src/main/java/com/alibaba/cola/statemachine/impl/TransitionImpl.java:121

    }

    @Override
    public boolean equals(Object anObject){
        if(anObject instanceof Transition){
            Transition other = (Transition)anObject;
            if(this.event.equals(other.getEvent())
                    && this.source.equals(other.getSource())
                    && this.target.equals(other.getTarget())){
                return true;
            }
        }
        return false;
    }

    @Override
    public void verify() {
        if(type== TransitionType.INTERNAL && source != target) {
            throw new StateMachineException(String.format("Internal transition source state '%s' " +
                    "and target state '%s' must be same.", source, target));
        }
    }
}

View on GitHub (pinned to 352e1a8675)

Solutions

  1. Make source and target identical for internal transitions: internal().from(S).to(S).event(E)
  2. Use a normal (external/local) transition instead of internal if the state should actually change
  3. Split the transition into separate transitions per state if it was meant to apply to multiple source states

Example fix

// before
.internal().from(STATE_A).to(STATE_B).event(EVENT_X) // throws on verify

// after
.internal().from(STATE_A).to(STATE_A).event(EVENT_X) // or external .from(A).to(B)
Defensive patterns

Strategy: validation

Validate before calling

// when building, enforce source.equals(target) for internal transitions
if (transitionType == INTERNAL && !source.equals(target)) {
    throw new IllegalStateException("internal transition must have source == target");
}

Try / catch

try {
    sm.fireEvent(source, event, ctx);
} catch (StateMachineException e) {
    if (e.getMessage().contains("must be same")) {
        // fix the transition definition: internal() requires from == to
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: 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().

Common situations: 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().

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of alibaba/COLA@352e1a8675 (2026-09-08). Data as JSON: /api/errors/c7bc3a63363b7cf4. Report an issue: GitHub.