temporalio/temporal · error
ConvertGradualChange can only be used with scalar types for
Error message
ConvertGradualChange can only be used with scalar types for now
What it means
ConvertGradualChange converts a gradual-change dynamic config value to T, but the generic implementation only supports scalar types (and slices of them per the switch); any other generic instantiation hits the default branch and panics. The panic occurs at conversion time when the setting is consumed, typically during static initialization.
Source
Thrown at common/dynamicconfig/gradual_change.go:112
if s, err := convertString(v); err == nil {
var change GradualChange[T]
reflect.ValueOf(&change.New).Elem().SetString(s)
return change, nil
}
return changeConverter(v)
}
case reflect.TypeFor[time.Duration]():
return func(v any) (GradualChange[T], error) {
if d, err := convertDuration(v); err == nil {
var change GradualChange[T]
reflect.ValueOf(&change.New).Elem().SetInt(int64(d))
return change, nil
}
return changeConverter(v)
}
default:
// nolint:forbidigo // this will be triggered from a static initializer before it can be triggered from production code
panic("ConvertGradualChange can only be used with scalar types for now")
}
}
// SubscribeGradualChange is a helper that allows subscribing to a GradualChange[T] as if it
// was a T. It handles setting a timer for when the setting may change in the future.
func SubscribeGradualChange[T any](
subscribable TypedSubscribable[GradualChange[T]],
changeKey []byte,
callback func(T),
timeSource clock.TimeSource,
) (T, func()) {
w := &gradualChangeSubscribeWrapper[T]{changeKey: changeKey, callback: callback, clock: timeSource}
w.lock.Lock()
w.change, w.cancelSub = subscribable(w.changeCallback)
val, _ := w.reevalLocked()
w.lock.Unlock()
View on GitHub (pinned to bde624efd1)
Solutions
- Instantiate ConvertGradualChange only with scalar types (bool, int variants, float, string)
- For non-scalar settings, subscribe to the raw GradualChange[T] value instead of converting
- Extend the switch in gradual_change.go to support the needed kind if the library owner approves
Example fix
// before var v = ConvertGradualChange[MyStruct](setting) // after var v = ConvertGradualChange[bool](setting) // or use SubscribeGradualChange for complex types
Defensive patterns
Strategy: type-guard
Type guard
isScalar[T]()
Prevention
- Scalar-only generic instantiation
- Use raw subscription for complex types
When it happens
Trigger: Calling ConvertGradualChange[T] (directly or via helpers like MatchingUseNewMatcher / MatchingEnableFairness wrappers) with a T that is not a scalar — e.g. a struct, map, or pointer type.
Common situations: A developer writes a new gradual-change config for a custom struct type assuming generics support it; refactoring moves an existing scalar call site to a struct-based type.
Related errors
- Can't deep copy non-zero time.Time: %v
- Can't deep copy value of type %s: %v
- dynamicconfig.New*Setting must only be called from static in
- duplicate registration of dynamic config key: %q
- unsupported error type: %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/7658e4135d2ec450.
Report an issue: GitHub.