{"record":{"id":"dd43a119e2549a77","repo":"vitessio/vitess","slug":"already-adapted-a-getter-for-key","errorCode":null,"errorMessage":"already adapted a getter for key ","messagePattern":"already adapted a getter for key ","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/viperutil/internal/sync/sync.go","lineNumber":332,"sourceCode":"}\n\n// end implementation of registry.Bindable for sync.Viper\n\n// AdaptGetter wraps a get function (matching the signature of\n// viperutil.Options.GetFunc) to be threadsafe with the passed-in synced Viper.\n//\n// It must be called prior to starting a watch on the synced Viper; it will\n// panic if a watch has already been established.\n//\n// This function must be called at most once per key; it will panic if attempting\n// to adapt multiple getters for the same key.\nfunc AdaptGetter[T any](key string, getter func(v *viper.Viper) func(key string) T, v *Viper) func(key string) T {\n\tif v.watchingConfig {\n\t\tpanic(\"cannot adapt getter to synchronized viper which is already watching a config\")\n\t}\n\n\tif _, ok := v.keys[key]; ok {\n\t\tpanic(\"already adapted a getter for key \" + key)\n\t}\n\n\tvar m sync.RWMutex\n\tv.keys[key] = &m\n\n\treturn func(key string) T {\n\t\tm.RLock()\n\t\tdefer m.RUnlock()\n\n\t\treturn getter(v.live)(key)\n\t}\n}\n","sourceCodeStart":314,"sourceCodeEnd":345,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/viperutil/internal/sync/sync.go#L314-L345","documentation":"AdaptGetter allows at most one getter per key per synchronized viper; the keys map tracks registrations. Registering the same key twice would leave two getters contending for one mutex slot, so it panics instead.","triggerScenarios":"Calling sync.AdaptGetter twice with the same key string on the same *Viper instance, e.g. duplicate registrations from two init() functions or two calls in a retry/loop.","commonSituations":"Two packages both register a getter for the same config key in their init(); accidentally re-running registration code on restart-in-process; copy-pasted registration lines.","solutions":["Register each key exactly once; audit init() functions for duplicate AdaptGetter calls for the same key","If you need to re-register, use a new Viper instance or check ownership before adapting","Uniquify the key (e.g. prefix with package/feature name) if two components genuinely need distinct getters"],"exampleFix":"// before\nsync.AdaptGetter[int](\"pool_size\", g, v)\nsync.AdaptGetter[int](\"pool_size\", g2, v) // panics\n// after\nsync.AdaptGetter[int](\"pool_size\", g, v) // one registration per key","handlingStrategy":"validation","validationCode":"var adapted sync.Map\nfunc adaptOnce(key string, fn func()) { if _, ok := adapted.LoadOrStore(key, true); !ok { fn() } }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One registration per key; centralize all AdaptGetter calls","Use LoadOrStore-style idempotent guards for package-level init registration","Grep your codebase for duplicate key strings passed to AdaptGetter"],"tags":["go","panic","config","duplicate-registration"],"backgroundTag":"duplicate-key-registration","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}