{"record":{"id":"356abc612409b134","repo":"temporalio/temporal","slug":"dynamicconfig-new-setting-must-only-be-called-from","errorCode":null,"errorMessage":"dynamicconfig.New*Setting must only be called from static initializers","messagePattern":"dynamicconfig\\.New\\*Setting must only be called from static initializers","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/dynamicconfig/registry.go","lineNumber":21,"sourceCode":"import (\n\t\"fmt\"\n\t\"sync/atomic\"\n)\n\ntype (\n\tregistry struct {\n\t\tsettings map[Key]GenericSetting\n\t\tqueried  atomic.Bool\n\t}\n)\n\nvar (\n\tglobalRegistry registry\n)\n\nfunc register(s GenericSetting) {\n\tif globalRegistry.queried.Load() {\n\t\tpanic(\"dynamicconfig.New*Setting must only be called from static initializers\")\n\t}\n\tif globalRegistry.settings == nil {\n\t\tglobalRegistry.settings = make(map[Key]GenericSetting)\n\t}\n\tif globalRegistry.settings[s.Key()] != nil {\n\t\t// nolint:forbidigo // only called during static initialization\n\t\tpanic(fmt.Sprintf(\"duplicate registration of dynamic config key: %q\", s.Key().String()))\n\t}\n\tglobalRegistry.settings[s.Key()] = s\n}\n\nfunc queryRegistry(k Key) GenericSetting {\n\tif !globalRegistry.queried.Load() {\n\t\tglobalRegistry.queried.Store(true)\n\t}\n\treturn globalRegistry.settings[k]\n}\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/dynamicconfig/registry.go#L3-L39","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nfunc handler() {\n    s := dynamicconfig.NewBoolSetting(\"my.flag\", false, ...)\n    ...\n}\n// after\nvar myFlag = dynamicconfig.NewBoolSetting(\"my.flag\", false, ...)\n\nfunc handler() { ... use myFlag ... }","handlingStrategy":"validation","validationCode":"// declare settings at package level only","typeGuard":null,"tryCatchPattern":null,"preventionTips":["No lazy setting construction","Top-level var declarations only"],"tags":["go","panic","dynamicconfig","registry","initialization-order"],"backgroundTag":"initialization-order-violation","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}