{"record":{"id":"98cb08d2f14a63b4","repo":"temporalio/temporal","slug":"w-state-transition-count-ref-transition-count","errorCode":null,"errorMessage":"%w: state transition count < ref transition count: %v < %v","messagePattern":"%w: state transition count < ref transition count: (.+?) < (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/transitionhistory/transition_history.go","lineNumber":107,"sourceCode":"\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,\n\t\t\trefVersionedTransition.TransitionCount,\n\t\t)\n\t}\n\tif minTransitionCount > refVersionedTransition.TransitionCount || maxTransitionCount < refVersionedTransition.TransitionCount {\n\t\treturn fmt.Errorf(\n\t\t\t\"%w: ref transition count out of range for version %v: %v not in [%v, %v]\",\n\t\t\tconsts.ErrStaleReference,\n\t\t\trefVersionedTransition.NamespaceFailoverVersion,\n\t\t\trefVersionedTransition.TransitionCount,\n\t\t\tminTransitionCount,\n\t\t\tmaxTransitionCount,\n\t\t)\n\t}\n\treturn nil\n}","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/transitionhistory/transition_history.go#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"func isStaleState(err error) bool {\n    return errors.Is(err, consts.ErrStaleState)\n}\n// pre-check before applying events\nif last := history[len(history)-1]; last.TransitionCount < ref.TransitionCount {\n    // state behind: fetch newer state first\n}","typeGuard":null,"tryCatchPattern":"err := applyBackfillEvents(ctx, state, events)\nif errors.Is(err, consts.ErrStaleState) {\n    return pollForCurrentMutableState(ctx, workflowKey) // wait for replication to catch up\n}\nreturn err","preventionTips":["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."],"tags":["go","replication","transition-count","versioning"],"backgroundTag":"stale-state-transition-count","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}