{"record":{"id":"c7e5232799f302e2","repo":"vitessio/vitess","slug":"you-ve-already-registered-a-function","errorCode":null,"errorMessage":"You've already registered a function","messagePattern":"You've already registered a function","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/stats/export.go","lineNumber":84,"sourceCode":"}\n\n// StatsAllStr is the consolidated name if a dimension gets combined.\nconst StatsAllStr = \"all\"\n\n// NewVarHook is the type of a hook to export variables in a different way\ntype NewVarHook func(name string, v expvar.Var)\n\ntype varGroup struct {\n\tsync.Mutex\n\tvars       map[string]expvar.Var\n\tnewVarHook NewVarHook\n}\n\nfunc (vg *varGroup) register(nvh NewVarHook) {\n\tvg.Lock()\n\tdefer vg.Unlock()\n\tif vg.newVarHook != nil {\n\t\tpanic(\"You've already registered a function\")\n\t}\n\tif nvh == nil {\n\t\tpanic(\"nil not allowed\")\n\t}\n\tvg.newVarHook = nvh\n\t// Call hook on existing vars because some might have been\n\t// created before the call to register\n\tfor k, v := range vg.vars {\n\t\tnvh(k, v)\n\t}\n\tvg.vars = nil\n}\n\nfunc (vg *varGroup) publish(name string, v expvar.Var) {\n\tif isVarDropped(name) {\n\t\treturn\n\t}\n\tvg.Lock()","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/stats/export.go#L66-L102","documentation":"stats.varGroup.register panics if a NewVarHook is registered more than once for the same var group. The hook is a process-wide singleton (e.g. stats.NewVarHook or package-level registration), so a second registration means two libraries are fighting over var creation callbacks. It also panics on a nil hook.","triggerScenarios":"Calling stats.RegisterNewVarHook (or vg.register) twice in one process, e.g. two packages/tests both installing a hook, or an init() that runs again due to duplicate wiring in tests.","commonSituations":"Test binaries importing multiple packages that each call the hook registration in init(); integrating a new observability library while an existing one already registered the hook; accidental double-invocation in setup code.","solutions":["Register the hook exactly once per process — guard with a sync.Once or a package-level 'registered' flag","Check which package already registers the hook and remove or consolidate the duplicate registration","In tests, reset/isolate via the stats package's test helpers instead of re-registering"],"exampleFix":"// before\nstats.RegisterNewVarHook(hook) // called in two packages -> panics\n// after\nvar once sync.Once\nfunc registerHook() {\n    once.Do(func() { stats.RegisterNewVarHook(hook) })\n}","handlingStrategy":"validation","validationCode":"var hookOnce sync.Once\nfunc ensureHook() {\n    hookOnce.Do(func() { stats.RegisterNewVarHook(hook) })\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register the NewVarHook in exactly one place (e.g. main or a single init)","Use sync.Once for hook registration","Audit test packages for competing init()-time registrations"],"tags":["go","panic","stats","initialization"],"backgroundTag":"duplicate-registration","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}