{"record":{"id":"7658e4135d2ec450","repo":"temporalio/temporal","slug":"convertgradualchange-can-only-be-used-with-scalar","errorCode":null,"errorMessage":"ConvertGradualChange can only be used with scalar types for now","messagePattern":"ConvertGradualChange can only be used with scalar types for now","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/dynamicconfig/gradual_change.go","lineNumber":112,"sourceCode":"\t\t\tif s, err := convertString(v); err == nil {\n\t\t\t\tvar change GradualChange[T]\n\t\t\t\treflect.ValueOf(&change.New).Elem().SetString(s)\n\t\t\t\treturn change, nil\n\t\t\t}\n\t\t\treturn changeConverter(v)\n\t\t}\n\tcase reflect.TypeFor[time.Duration]():\n\t\treturn func(v any) (GradualChange[T], error) {\n\t\t\tif d, err := convertDuration(v); err == nil {\n\t\t\t\tvar change GradualChange[T]\n\t\t\t\treflect.ValueOf(&change.New).Elem().SetInt(int64(d))\n\t\t\t\treturn change, nil\n\t\t\t}\n\t\t\treturn changeConverter(v)\n\t\t}\n\tdefault:\n\t\t// nolint:forbidigo // this will be triggered from a static initializer before it can be triggered from production code\n\t\tpanic(\"ConvertGradualChange can only be used with scalar types for now\")\n\t}\n}\n\n// SubscribeGradualChange is a helper that allows subscribing to a GradualChange[T] as if it\n// was a T. It handles setting a timer for when the setting may change in the future.\nfunc SubscribeGradualChange[T any](\n\tsubscribable TypedSubscribable[GradualChange[T]],\n\tchangeKey []byte,\n\tcallback func(T),\n\ttimeSource clock.TimeSource,\n) (T, func()) {\n\tw := &gradualChangeSubscribeWrapper[T]{changeKey: changeKey, callback: callback, clock: timeSource}\n\n\tw.lock.Lock()\n\tw.change, w.cancelSub = subscribable(w.changeCallback)\n\tval, _ := w.reevalLocked()\n\tw.lock.Unlock()\n","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/dynamicconfig/gradual_change.go#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar v = ConvertGradualChange[MyStruct](setting)\n// after\nvar v = ConvertGradualChange[bool](setting) // or use SubscribeGradualChange for complex types","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"isScalar[T]()","tryCatchPattern":null,"preventionTips":["Scalar-only generic instantiation","Use raw subscription for complex types"],"tags":["go","panic","generics","dynamicconfig"],"backgroundTag":"unsupported-generic-type","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}