temporalio/temporal · error
ErrStaleReference
ErrStaleReference
Error message
%w: %w
What it means
validateStateMachineRef fails when a stored state-machine reference can no longer be resolved: the HSM child node for ref.StateMachinePath() is not found. It wraps hsm.ErrStateMachineNotFound with consts.ErrStaleReference, meaning the reference points at state that no longer exists (deleted, rebuilt, or never replicated).
Solutions
- Treat as stale: discard the timer/task; the state machine it targeted no longer exists
- Check replication lag or state rebuild events for the workflow if this occurs unexpectedly
- If the node should exist, inspect the workflow's mutable state / transition-history settings for a rebuild that dropped the node
Defensive patterns
Strategy: try-catch
Type guard
func isStaleRef(err error) bool { return errors.Is(err, consts.ErrStaleReference) } Try / catch
err := env.validateStateMachineRef(ms, ref, stale)
if err != nil && errors.Is(err, consts.ErrStaleReference) {
// discard the timer/task; ref points at deleted state
return nil
} Prevention
- Re-validate refs immediately before use; keep ref lifetimes short
- Detect and discard tasks for already-deleted state machines
- Monitor mutable-state rebuild frequency
When it happens
Trigger: executeSingleStateMachineTimer (or another caller) validates a task's state machine ref; ms.HSM().Child(ref.StateMachinePath()) returns hsm.ErrStateMachineNotFound. With transition history enabled, this is unconditionally treated as stale.
Common situations: State machine deleted (e.g. activity canceled/completed) between ref generation and timer execution; mutable state rebuilt without transition history; cross-cluster replication lag so the node hasn't arrived yet.
Related errors
- ErrStaleState
- Failed to get HSM operation log
- %w: %w
- : zombie workflow cannot be updated
- ErrInvalidTransition
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/7268096463ab0edb.
Report an issue: GitHub.
Appendix: source
Thrown at service/history/statemachine_environment.go:254
(ref.StateMachineRef.MachineLastUpdateVersionedTransition != nil &&
ref.StateMachineRef.MachineLastUpdateVersionedTransition.TransitionCount == 0) ||
len(ms.GetExecutionInfo().TransitionHistory) == 0 {
// Transtion history was disabled when the ref is generated,
// fallback to the old validation logic.
return e.validateStateMachineRefWithoutTransitionHistory(ms, ref, potentialStaleState)
}
err := transitionhistory.StalenessCheck(
ms.GetExecutionInfo().GetTransitionHistory(),
ref.StateMachineRef.MutableStateVersionedTransition,
)
if err != nil {
return err
}
node, err := ms.HSM().Child(ref.StateMachinePath())
if err != nil {
if errors.Is(err, hsm.ErrStateMachineNotFound) {
return fmt.Errorf("%w: %w", consts.ErrStaleReference, err)
}
return fmt.Errorf("%w: %w", serviceerror.NewInternal("node lookup failed"), err)
}
if node.InternalRepr().GetInitialVersionedTransition().TransitionCount == 0 {
// transition history was disabled after the ref was generated and mutable state got rebuilt.
// fallback to the old validation logic.
return e.validateStateMachineRefWithoutTransitionHistory(ms, ref, potentialStaleState)
}
if transitionhistory.Compare(
ref.StateMachineRef.MachineInitialVersionedTransition,
node.InternalRepr().GetInitialVersionedTransition(),
) != 0 {
return fmt.Errorf("%w: initial versioned transition mismatch", consts.ErrStaleReference)
}
if ref.StateMachineRef.GetMachineLastUpdateVersionedTransition().GetTransitionCount() == 0 {View on GitHub (pinned to bde624efd1)