{"record":{"id":"7eb65ee41fa46abc","repo":"temporalio/temporal","slug":"errinvalidtransition","errorCode":"ErrInvalidTransition","errorMessage":"%w from %v","messagePattern":"%w from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"chasm/statemachine.go","lineNumber":71,"sourceCode":"\tprevState := sm.StateMachineState()\n\n\t// Defer to always emit the transition telemetry event.\n\tif telemetry.DebugMode() {\n\t\tdefer func() {\n\t\t\tattrs := []attribute.KeyValue{\n\t\t\t\tattribute.String(\"chasm.transition.source\", fmt.Sprintf(\"%v\", prevState)),\n\t\t\t\tattribute.String(\"chasm.transition.destination\", fmt.Sprintf(\"%v\", t.Destination)),\n\t\t\t}\n\t\t\tif retErr != nil {\n\t\t\t\tattrs = append(attrs, attribute.String(\"chasm.transition.error\", retErr.Error()))\n\t\t\t}\n\t\t\tspan := trace.SpanFromContext(ctx.goContext())\n\t\t\tspan.AddEvent(\"chasm.transition\", trace.WithAttributes(attrs...))\n\t\t}()\n\t}\n\n\tif !t.Possible(sm) {\n\t\treturn fmt.Errorf(\"%w from %v\", ErrInvalidTransition, prevState)\n\t}\n\n\tif err := t.apply(sm, ctx, event); err != nil {\n\t\treturn err\n\t}\n\tsm.SetStateMachineState(t.Destination)\n\treturn nil\n}\n","sourceCodeStart":53,"sourceCodeEnd":80,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/chasm/statemachine.go#L53-L80","documentation":"Transition.Apply in the CHASM state machine library returns this error when the current state of a StateMachine is not among the transition's declared Sources. It wraps the sentinel ErrInvalidTransition (a FailedPrecondition serviceerror) with the previous state value so callers can see which state blocked the transition. The library throws it to prevent applying an event whose transition is undefined from the current state.","triggerScenarios":"Calling Transition.Apply(sm, ctx, event) when sm.StateMachineState() is not in t.Sources (checked via Possible), i.e. applying an event to a state machine in a state the transition was not declared for.","commonSituations":"Double-processing an event that already moved the machine (e.g. replayed history or a duplicated task), transition tables missing a legitimate source state, or concurrent handlers racing to transition the same machine where only one expected path is valid.","solutions":["Check sm.StateMachineState() (or Possible) before Apply and treat the invalid transition as an expected no-op where idempotent","Add the missing state to the transition's Sources if the state is legitimately reachable","Use errors.Is(err, chasm.ErrInvalidTransition) to distinguish expected invalid transitions from real apply failures and log/alert only on unexpected ones"],"exampleFix":"// before\nerr := transition.Apply(sm, ctx, event)\nif err != nil {\n  return err\n}\n// after\nif !transition.Possible(sm) {\n  return nil // already transitioned; idempotent no-op\n}\nerr := transition.Apply(sm, ctx, event)\nif err != nil {\n  return err\n}","handlingStrategy":"try-catch","validationCode":"// Go\nif !transition.Possible(sm) {\n  // expected: skip or no-op\n  return nil\n}","typeGuard":"func canApply[S comparable, SM interface{ StateMachineState() S }](t TransitionLike[S,SM], sm SM) bool { return slices.Contains(t.Sources(), sm.StateMachineState()) }","tryCatchPattern":"if err := t.Apply(sm, ctx, event); err != nil {\n  if errors.Is(err, chasm.ErrInvalidTransition) {\n    logger.Info(\"transition not valid from current state\", \"state\", sm.StateMachineState())\n    return nil // idempotent path\n  }\n  return err\n}","preventionTips":["Always check Possible() before Apply when the transition is expected to be conditional","Design transitions to be idempotent for replayed/duplicated events","Use errors.Is with ErrInvalidTransition to classify expected vs unexpected failures","Enumerate all reachable source states when defining transition tables"],"tags":["go","state-machine","chasm","invalid-transition"],"backgroundTag":"invalid-state-transition","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}