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

The mirror image of the stale-state case: when the reference transition's namespace failover version is OLDER than everything in the state's transition history, the state is actually newer than the reference, so StalenessCheck returns consts.ErrStaleReference wrapped with both versions. This tells callers that the incoming reference (event/replication task) is outdated and must not be applied to the current state.

Source

Thrown at common/persistence/transitionhistory/transition_history.go:99

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,
			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,

View on GitHub (pinned to bde624efd1)

Solutions

  1. Drop/ignore the stale reference: replication logic treats ErrStaleReference as a signal to skip the event rather than apply it.
  2. Ensure the source cluster has learned about the namespace failover (namespace replication is bidirectional); check namespace replication status.
  3. Verify replication task ordering/dedup on the receiving cluster; drain old replication queues after failover.
  4. If events should legitimately be applied, use backfill APIs (backfill history events) which account for versioning instead of raw replication.
Defensive patterns

Strategy: type-guard

Validate before calling

func isStaleReference(err error) bool {
    return errors.Is(err, consts.ErrStaleReference)
}

Type guard

func isStaleReferenceErr(err error) bool { return errors.Is(err, consts.ErrStaleReference) }

Try / catch

err := transitionhistory.ReplicateVersionedTransition(state, ref)
if errors.Is(err, consts.ErrStaleReference) {
    return nil // safe to skip an outdated replication task
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling StalenessCheck (via IsStale, applyBackfillEvents, ReplicateVersionedTransition, GetOrPollWorkflowMutableState) with refVersionedTransition.NamespaceFailoverVersion less than the last history item's version and idx == -1.

Common situations: Duplicate or out-of-order replication tasks delivering pre-failover events after the namespace already failed over; replaying an old replication task queue after failover; source cluster not aware the namespace failed over on the target.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/d6e08ff37782574b. Report an issue: GitHub.