{"record":{"id":"ea16ac0dad7566fb","repo":"temporalio/temporal","slug":"w-state-namespace-failover-version-ref-namespa","errorCode":null,"errorMessage":"%w: state namespace failover version < ref namespace failover version: %v < %v","messagePattern":"%w: state namespace failover version < ref namespace failover version: (.+?) < (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/transitionhistory/transition_history.go","lineNumber":92,"sourceCode":"// that the state is not stale or that the task/request itself is not stale. For example, if the state has a history of\n// `[{v: 1, t: 3}, {v: 2, t: 5}]`, task A `{v: 2, t: 4}` **is not** referencing stale state because for version `2`\n// transitions `4-5` are valid, while task B `{v: 2, t: 6}` **is** referencing stale state because the transition count\n// is out of range for version `2`. Furthermore, task C `{v: 1, t: 4}` itself is stale because it is referencing an\n// impossible state, likely due to post split-brain reconciliation.\n// NOTE: This function should only be used when there is reloading logic on top of it, since the error returned is a\n// terminal error.\nfunc StalenessCheck(\n\thistory []*persistencespb.VersionedTransition,\n\trefVersionedTransition *persistencespb.VersionedTransition,\n) error {\n\tif len(history) == 0 {\n\t\treturn serviceerror.NewInternal(\"state has empty transition history\")\n\t}\n\tidx, minTransitionCount, maxTransitionCount := transitionHistoryRangeForVersion(history, refVersionedTransition.NamespaceFailoverVersion)\n\tif idx == -1 {\n\t\tlastItem := history[len(history)-1]\n\t\tif lastItem.NamespaceFailoverVersion < refVersionedTransition.NamespaceFailoverVersion {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"%w: state namespace failover version < ref namespace failover version: %v < %v\",\n\t\t\t\tconsts.ErrStaleState,\n\t\t\t\tlastItem.NamespaceFailoverVersion,\n\t\t\t\trefVersionedTransition.NamespaceFailoverVersion,\n\t\t\t)\n\t\t}\n\t\treturn fmt.Errorf(\n\t\t\t\"%w: state namespace failover version > ref namespace failover version: %v > %v\",\n\t\t\tconsts.ErrStaleReference,\n\t\t\tlastItem.NamespaceFailoverVersion,\n\t\t\trefVersionedTransition.NamespaceFailoverVersion,\n\t\t)\n\t}\n\tif idx == len(history)-1 && maxTransitionCount < refVersionedTransition.TransitionCount {\n\t\treturn fmt.Errorf(\n\t\t\t\"%w: state transition count < ref transition count: %v < %v\",\n\t\t\tconsts.ErrStaleState,\n\t\t\tmaxTransitionCount,","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/transitionhistory/transition_history.go#L74-L110","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"func isStaleState(err error) bool {\n    return errors.Is(err, consts.ErrStaleState)\n}","typeGuard":"func isStaleStateErr(err error) bool { return errors.Is(err, consts.ErrStaleState) }","tryCatchPattern":"stale, err := transitionhistory.IsStale(state, ref)\nif errors.Is(err, consts.ErrStaleState) {\n    // state is behind: re-fetch mutable state from source cluster\n    return refreshMutableStateFromSource(ctx, workflowKey)\n}\nif err != nil { return err }","preventionTips":["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."],"tags":["go","replication","namespace-failover","versioning"],"backgroundTag":"stale-state-failover-version","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}