vitessio/vitess · error
cannot adapt getter to synchronized viper which is already w
Error message
cannot adapt getter to synchronized viper which is already watching a config
What it means
AdaptGetter registers a dynamic getter for a config key on a synchronized viper wrapper. It panics if the wrapper has already started watching a config file, because adapting getters after the watch begins would leave the getter registry in an inconsistent state. This is an initialization-order contract: all getters must be adapted before NewDynamic establishes the watch.
Source
Thrown at go/viperutil/internal/sync/sync.go:328
func (v *Viper) SetDefault(key string, value any) {
v.disk.SetDefault(key, value)
v.live.SetDefault(key, value)
}
// end implementation of registry.Bindable for sync.Viper
// AdaptGetter wraps a get function (matching the signature of
// viperutil.Options.GetFunc) to be threadsafe with the passed-in synced Viper.
//
// It must be called prior to starting a watch on the synced Viper; it will
// panic if a watch has already been established.
//
// This function must be called at most once per key; it will panic if attempting
// to adapt multiple getters for the same key.
func AdaptGetter[T any](key string, getter func(v *viper.Viper) func(key string) T, v *Viper) func(key string) T {
if v.watchingConfig {
panic("cannot adapt getter to synchronized viper which is already watching a config")
}
if _, ok := v.keys[key]; ok {
panic("already adapted a getter for key " + key)
}
var m sync.RWMutex
v.keys[key] = &m
return func(key string) T {
m.RLock()
defer m.RUnlock()
return getter(v.live)(key)
}
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Move the AdaptGetter call before the NewDynamic/watch-establishing call in startup order
- Adapt all getters during package init or early main() before any config watch starts
- If the key is adapted conditionally, ensure every AdaptGetter path runs before the watch (e.g. register all keys upfront with defaults)
Example fix
// before
watched := sync.NewDynamic("config")
getter := sync.AdaptGetter[string]("key", f, watched) // panics
// after
getter := sync.AdaptGetter[string]("key", f, watched)
watched := sync.NewDynamic("config") Defensive patterns
Strategy: validation
Validate before calling
func canAdapt(v *sync.Viper) bool { return !v.WatchingConfig() } // call before AdaptGetter; ensure registration happens before NewDynamic Prevention
- Adapt all getters in one early init phase, before any watch starts
- Centralize config getter registration in a single function called from main()
- Never adapt getters lazily inside handlers or request paths
When it happens
Trigger: Calling sync.AdaptGetter after NewDynamic (or any call that sets watchingConfig=true) has already started watching the config; adapting getters lazily at request time instead of during startup initialization.
Common situations: Developers add a new dynamic config getter in application code that runs after servenv/logutil init has already called NewDynamic and started watching; refactors move an AdaptGetter call into a later initialization phase or a request handler.
Related errors
- GetFuncForType does not support array types
- GetFuncForType does not support channel types
- GetFuncForType does not support function types
- GetFuncForType does not support interface types (specify a s
- no default GetFunc for type %T; call Configure with a custom
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/9b43096a779214e6.
Report an issue: GitHub.