temporalio/temporal · error
%w: state namespace failover version < ref namespace failove
Error message
%w: state namespace failover version < ref namespace failover version: %v < %v
What it means
StalenessCheck compares a workflow mutable state's versioned transition history against a reference transition (from a remote replication task or backfill event). If the reference's namespace failover version is newer than every entry in the state's history, the state is outdated, so it returns consts.ErrStaleState wrapped with the two versions. This prevents applying events to a workflow state that predates the namespace's current failover epoch.
Source
Thrown at common/persistence/transitionhistory/transition_history.go:92
// that the state is not stale or that the task/request itself is not stale. For example, if the state has a history of
// `[{v: 1, t: 3}, {v: 2, t: 5}]`, task A `{v: 2, t: 4}` **is not** referencing stale state because for version `2`
// transitions `4-5` are valid, while task B `{v: 2, t: 6}` **is** referencing stale state because the transition count
// is out of range for version `2`. Furthermore, task C `{v: 1, t: 4}` itself is stale because it is referencing an
// impossible state, likely due to post split-brain reconciliation.
// NOTE: This function should only be used when there is reloading logic on top of it, since the error returned is a
// terminal error.
func StalenessCheck(
history []*persistencespb.VersionedTransition,
refVersionedTransition *persistencespb.VersionedTransition,
) error {
if len(history) == 0 {
return serviceerror.NewInternal("state has empty transition history")
}
idx, minTransitionCount, maxTransitionCount := transitionHistoryRangeForVersion(history, refVersionedTransition.NamespaceFailoverVersion)
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,View on GitHub (pinned to bde624efd1)
Solutions
- Let the replication machinery refresh the mutable state: stale-state errors are handled by re-fetching state from the source cluster (GetOrPollWorkflowMutableState will resolve).
- Verify both clusters' namespace failover versions are consistent (namespace replication succeeded on the target).
- If caused by a restore, restore to a point consistent with current namespace failover versions or re-register/replicate the namespace.
- Check history shard ownership/shard splitting isn't serving stale state after failover.
Defensive patterns
Strategy: type-guard
Validate before calling
func isStaleState(err error) bool {
return errors.Is(err, consts.ErrStaleState)
} Type guard
func isStaleStateErr(err error) bool { return errors.Is(err, consts.ErrStaleState) } Try / catch
stale, err := transitionhistory.IsStale(state, ref)
if errors.Is(err, consts.ErrStaleState) {
// state is behind: re-fetch mutable state from source cluster
return refreshMutableStateFromSource(ctx, workflowKey)
}
if err != nil { return err } Prevention
- Keep namespace replication in sync before processing workflow replication.
- Don't restore DB backups that predate namespace failover events.
- Monitor cross-cluster namespace failover version mismatch.
When it happens
Trigger: Calling StalenessCheck (via IsStale, GetOrPollWorkflowMutableState, applyBackfillEvents, ReplicateVersionedTransition) with refVersionedTransition.NamespaceFailoverVersion greater than the last history item's NamespaceFailoverVersion, while transitionHistoryRangeForVersion returns idx == -1 (no history entry covers the reference version).
Common situations: Replication lag: a history shard serving a replica state older than a namespace failover that already happened on the source cluster; restoring an old DB backup after a namespace failover; cross-cluster replication misconfiguration where clusters disagree on failover versions.
Related errors
- %w: state namespace failover version > ref namespace failove
- %w: state transition count < ref transition count: %v < %v
- ${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/ea16ac0dad7566fb.
Report an issue: GitHub.