temporalio/temporal · error
%w: state transition count < ref transition count: %v < %v
Error message
%w: state transition count < ref transition count: %v < %v
What it means
When the reference's failover version falls within the last history entry's version range, StalenessCheck additionally compares transition counts. If the state's maximum transition count in that range is below the reference's transition count, the state is behind the reference within the same failover epoch, and consts.ErrStaleState is returned wrapped with both counts.
Source
Thrown at common/persistence/transitionhistory/transition_history.go:107
if idx == -1 {
lastItem := history[len(history)-1]
if lastItem.NamespaceFailoverVersion < refVersionedTransition.NamespaceFailoverVersion {
return fmt.Errorf(
"%w: state namespace failover version < ref namespace failover version: %v < %v",
consts.ErrStaleState,
lastItem.NamespaceFailoverVersion,
refVersionedTransition.NamespaceFailoverVersion,
)
}
return fmt.Errorf(
"%w: state namespace failover version > ref namespace failover version: %v > %v",
consts.ErrStaleReference,
lastItem.NamespaceFailoverVersion,
refVersionedTransition.NamespaceFailoverVersion,
)
}
if idx == len(history)-1 && maxTransitionCount < refVersionedTransition.TransitionCount {
return fmt.Errorf(
"%w: state transition count < ref transition count: %v < %v",
consts.ErrStaleState,
maxTransitionCount,
refVersionedTransition.TransitionCount,
)
}
if minTransitionCount > refVersionedTransition.TransitionCount || maxTransitionCount < refVersionedTransition.TransitionCount {
return fmt.Errorf(
"%w: ref transition count out of range for version %v: %v not in [%v, %v]",
consts.ErrStaleReference,
refVersionedTransition.NamespaceFailoverVersion,
refVersionedTransition.TransitionCount,
minTransitionCount,
maxTransitionCount,
)
}
return nil
}View on GitHub (pinned to bde624efd1)
Solutions
- Retry after replication catches up: GetOrPollWorkflowMutableState handles this by polling the source until the state is current.
- Check replication task backlogs on the target cluster and process them in order.
- Verify event ordering/sequence handling in the replication stream; missing events should trigger a re-fetch of state from source.
- If persistently stale, check for lost replication tasks and trigger a state refresh or history repair from the source cluster.
Defensive patterns
Strategy: retry
Validate before calling
func isStaleState(err error) bool {
return errors.Is(err, consts.ErrStaleState)
}
// pre-check before applying events
if last := history[len(history)-1]; last.TransitionCount < ref.TransitionCount {
// state behind: fetch newer state first
} Try / catch
err := applyBackfillEvents(ctx, state, events)
if errors.Is(err, consts.ErrStaleState) {
return pollForCurrentMutableState(ctx, workflowKey) // wait for replication to catch up
}
return err Prevention
- Keep replication task processing in transition-count order per workflow.
- Alert on replication lag between clusters.
- Use GetOrPollWorkflowMutableState (which polls) instead of one-shot fetches before applying events.
When it happens
Trigger: Calling StalenessCheck (via IsStale, GetOrPollWorkflowMutableState, applyBackfillEvents, ReplicateVersionedTransition) where idx == len(history)-1 and maxTransitionCount < refVersionedTransition.TransitionCount — i.e. same failover version but the local state has fewer transitions than the incoming reference.
Common situations: Replication lag within the same failover epoch: target cluster's mutable state hasn't caught up with source transitions; concurrent replication tasks applied out of order; a shard replaying events against a partially-replicated state.
Related errors
- %w: state namespace failover version < ref namespace failove
- %w: state namespace failover version > ref namespace failove
- ${failure.Message}
- ErrStaleState
- task queue user data replication failed: %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/98cb08d2f14a63b4.
Report an issue: GitHub.