temporalio/temporal · error
dynamicconfig.New*Setting must only be called from static in
Error message
dynamicconfig.New*Setting must only be called from static initializers
What it means
The dynamicconfig registry records every New*Setting call and, once queryRegistry has run (globalRegistry.queried set), treats further registrations as a bug: settings must exist before the first query so the registry is a complete static inventory. Calling New*Setting after querying panics.
Source
Thrown at common/dynamicconfig/registry.go:21
import (
"fmt"
"sync/atomic"
)
type (
registry struct {
settings map[Key]GenericSetting
queried atomic.Bool
}
)
var (
globalRegistry registry
)
func register(s GenericSetting) {
if globalRegistry.queried.Load() {
panic("dynamicconfig.New*Setting must only be called from static initializers")
}
if globalRegistry.settings == nil {
globalRegistry.settings = make(map[Key]GenericSetting)
}
if globalRegistry.settings[s.Key()] != nil {
// nolint:forbidigo // only called during static initialization
panic(fmt.Sprintf("duplicate registration of dynamic config key: %q", s.Key().String()))
}
globalRegistry.settings[s.Key()] = s
}
func queryRegistry(k Key) GenericSetting {
if !globalRegistry.queried.Load() {
globalRegistry.queried.Store(true)
}
return globalRegistry.settings[k]
}
View on GitHub (pinned to bde624efd1)
Solutions
- Move the New*Setting call to a package-level var so it runs during static initialization
- If settings must vary at runtime, don't register them; read values with the non-registered lookup APIs
- In tests, declare settings in init()/package vars before any registry query occurs
Example fix
// before
func handler() {
s := dynamicconfig.NewBoolSetting("my.flag", false, ...)
...
}
// after
var myFlag = dynamicconfig.NewBoolSetting("my.flag", false, ...)
func handler() { ... use myFlag ... } Defensive patterns
Strategy: validation
Validate before calling
// declare settings at package level only
Prevention
- No lazy setting construction
- Top-level var declarations only
When it happens
Trigger: Constructing a dynamicconfig setting (NewBoolSetting, NewIntSetting, etc.) lazily at runtime — inside a request handler, a function called after server start, or a lazily initialized package var — after any code has queried the registry.
Common situations: Moving a setting declaration inside a function during refactoring; creating settings dynamically per-tenant/namespace; unit tests that build settings after the global registry was already queried by an earlier init.
Related errors
- duplicate registration of dynamic config key: %q
- Can't deep copy non-zero time.Time: %v
- Can't deep copy value of type %s: %v
- ConvertGradualChange can only be used with scalar types for
- unsupported error type: %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/356abc612409b134.
Report an issue: GitHub.