temporalio/temporal · error

unknown transaction policy: %v

Error message

unknown transaction policy: %v

What it means

During mutable-state task generation, code that applies a historyi.TransactionPolicy switches over the policy value (Active, Passive, ...) and panics on anything else with "unknown transaction policy: %v". TransactionPolicy tells the history service whether this node is the active cluster (should emit tasks/replication) or passive. A value outside the known set means a new enum member was added without updating this switch, or the policy was constructed incorrectly.

Source

Thrown at service/history/workflow/mutable_state_impl.go:8467

						NextEventID:            nextEventID,
						TaskEquivalents:        replicationTasks,
						LastVersionHistoryItem: lastVersionHistoryItem,
					}

					if ms.dbRecordVersion == 1 {
						syncVersionedTransitionTask.IsFirstTask = true
					}

					// versioned transition updated in the transaction
					ms.InsertTasks[tasks.CategoryReplication] = append(
						ms.InsertTasks[tasks.CategoryReplication],
						syncVersionedTransitionTask,
					)
				}
			}
		case historyi.TransactionPolicyPassive:
		default:
			panic(fmt.Sprintf("unknown transaction policy: %v", transactionPolicy))
		}
	} else if isWorkflow {
		ms.InsertTasks[tasks.CategoryReplication] = append(
			ms.InsertTasks[tasks.CategoryReplication],
			replicationTasks...,
		)
	} else {
		return softassert.UnexpectedInternalErr(ms.logger, "state-based replication not enabled for chasm execution", nil)
	}

	if transactionPolicy == historyi.TransactionPolicyPassive &&
		len(ms.InsertTasks[tasks.CategoryReplication]) > 0 {
		return softassert.UnexpectedInternalErr(
			ms.logger,
			"should not generate replication task when close transaction as passive",
			nil,
		)
	}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Upgrade all history service binaries to the same version so every TransactionPolicy value is handled everywhere.
  2. Search the codebase for the new TransactionPolicy constant and add the missing case to this switch.
  3. Audit the caller that passed the policy value to confirm it isn't passing an uninitialized/zero-value policy.
  4. If it reproduces on a single version, file a bug with the printed policy value.

Example fix

// before
case historyi.TransactionPolicyPassive:
default:
    panic(fmt.Sprintf("unknown transaction policy: %v", transactionPolicy))
// after
case historyi.TransactionPolicyPassive:
case historyi.TransactionPolicyActive: // handle new value explicitly
default:
    panic(fmt.Sprintf("unknown transaction policy: %v", transactionPolicy))
Defensive patterns

Strategy: validation

Validate before calling

// Before invoking task generation, assert the policy is one of the known values:
if transactionPolicy != historyi.TransactionPolicyActive &&
    transactionPolicy != historyi.TransactionPolicyPassive {
    return fmt.Errorf("unrecognized transaction policy %v", transactionPolicy)
}

Type guard

func knownTransactionPolicy(p historyi.TransactionPolicy) bool {
    switch p {
    case historyi.TransactionPolicyActive, historyi.TransactionPolicyPassive:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Generating replication tasks inside an HSM/transaction update path (around line 8467) when the transactionPolicy parameter holds an unrecognized value — e.g. a newly added TransactionPolicy constant not yet handled in this switch, or a policy passed through from an incompatible caller.

Common situations: Version skew where one binary produces a new policy value another binary's switch doesn't handle; internal refactoring bugs adding a new policy without updating all switches.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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