temporalio/temporal · critical
can't decode TweakablePolicies:
Error message
can't decode TweakablePolicies:
What it means
TweakablePolicies are captured via workflow.MutableSideEffect so all replayed executions see identical values. If the stored side-effect blob cannot be decoded into TweakablePolicies on replay, the workflow cannot proceed deterministically and panics. This indicates persisted data incompatible with the current struct definition.
Source
Thrown at service/worker/scheduler/workflow.go:1329
func (s *scheduler) checkConflict(token int64) error {
if token == 0 || token == s.State.ConflictToken {
return nil
}
return errUpdateConflict
}
func (s *scheduler) updateTweakables() {
// Use MutableSideEffect so that we can change the defaults without breaking determinism.
get := func(ctx workflow.Context) any {
p := CurrentTweakablePolicies
// Re-evaluates migration dynamic config each iteration.
p.EnableCHASMMigration = s.enableCHASMMigration()
p.MigrateWithRunningWorkflows = s.migrateWithRunningWorkflows()
return p
}
eq := func(a, b any) bool { return a.(TweakablePolicies) == b.(TweakablePolicies) }
if err := workflow.MutableSideEffect(s.ctx, "tweakables", get, eq).Get(&s.tweakables); err != nil {
panic("can't decode TweakablePolicies:" + err.Error())
}
}
func (s *scheduler) getCatchupWindow() time.Duration {
cw := s.Schedule.Policies.CatchupWindow
if cw == nil {
return s.tweakables.DefaultCatchupWindow
} else if cw.AsDuration() < s.tweakables.MinCatchupWindow {
return s.tweakables.MinCatchupWindow
} else {
return cw.AsDuration()
}
}
func (s *scheduler) resolveOverlapPolicy(overlapPolicy enumspb.ScheduleOverlapPolicy) enumspb.ScheduleOverlapPolicy {
if overlapPolicy == enumspb.SCHEDULE_OVERLAP_POLICY_UNSPECIFIED {
overlapPolicy = s.Schedule.Policies.OverlapPolicy
}View on GitHub (pinned to bde624efd1)
Solutions
- Run a worker version that matches the one that wrote the side-effect (version pinning / patching)
- Terminate and restart the schedule workflow so tweakables are re-captured from current schedule config
- Restore the workflow from a history point before the incompatible side-effect write
Defensive patterns
Strategy: fallback
Prevention
- Deploy worker and server upgrades atomically so side-effect writers and readers match
- Use workflow.Patched/version gates when mutating TweakablePolicies fields
- Avoid reusing one namespace's schedule workflow history across clusters with different code versions
- Test upgrades against a staging schedule with in-flight workflows
When it happens
Trigger: Raised in the scheduler workflow's tweakables loading path (service/worker/scheduler/workflow.go:1329) when workflow.MutableSideEffect(...).Get(&s.tweakables) returns a decode error — the stored blob was written by a different schema/version, or the value is corrupted/truncated.
Common situations: Upgrading Temporal across a TweakablePolicies struct change with an in-flight schedule; importing/replaying a workflow history produced by a mismatched version; corrupted persistence payload.
Related errors
- unexpected return type of decodeValueTyped (got: %T, expecte
- could not decode next time cache as proto or json
- failed to read version demotion signal configuration: %w
- registrable component validation error: CHASM search attribu
- registrable component validation error: CHASM search attribu
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/da5c25764de4b531.
Report an issue: GitHub.