apache/beam · error
RegisterProfCaptureHook
Error message
RegisterProfCaptureHook: %s registered twice
What it means
RegisterProfCaptureHook stores a CPU-profile CaptureHookFactory in a package-level registry keyed by name, and deliberately panics on duplicate registration. Because hooks are typically registered from package init functions, this fires at process startup when two packages (or two copies of the same package via different module versions) register the same hook identifier.
Solutions
- Find the duplicate registration call sites (grep for RegisterProfCaptureHook with the offending name) and remove one.
- Register the hook only once — prefer package init in a single package, not both init and main.
- If two module versions of a hook package are linked, deduplicate the dependency (go mod tidy / go mod graph) so only one init runs.
- If dynamic registration is needed, guard with a check against the registry or track registration state before calling.
Example fix
// before
perf.RegisterProfCaptureHook("myprof", myFactory) // called in both init() and main
// after
var profOnce sync.Once
func ensureProfHook() {
profOnce.Do(func() { perf.RegisterProfCaptureHook("myprof", myFactory) })
} Defensive patterns
Strategy: validation
Validate before calling
// guard before registering
if _, exists := perfRegistrySnapshot["myprof"]; !exists {
perf.RegisterProfCaptureHook("myprof", myFactory)
} Try / catch
func() (ok bool) {
defer func() {
if r := recover(); r != nil {
log.Printf("hook already registered: %v", r)
ok = true
}
}()
perf.RegisterProfCaptureHook("myprof", myFactory)
return true
}() Prevention
- Register each hook exactly once, in a single package init.
- Never register hooks from both init() and application main/test setup.
- Keep the hook package to a single module version in go.mod.
- Use sync.Once for any conditional registration path.
When it happens
Trigger: Calling perf.RegisterProfCaptureHook(name, factory) with a name already present in profCaptureHookRegistry — typically two init() functions in the dependency graph registering the same identifier, or an app registering a hook that a vendored package also registers.
Common situations: Importing two Beam x/hooks packages that both register the same profiler name; accidental double registration in test setup plus production init; Go module upgrades pulling two versions of a hook package that both run init.
Related errors
- RegisterHeapCaptureHook
- RegisterTraceCaptureHook
- Beam has not been initialized. Call beam.Init() before…
- Init hooks have already run. Register hook during init()…
- while executing Up for
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/897021c675649e1f.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/x/hooks/perf/perf.go:141
name, opts := hooks.Decode(h)
if err := heapCaptureHookRegistry[name](opts)(ctx, fmt.Sprintf("heap%s", req.InstructionId), &heapProfBuf); err != nil {
return err
}
}
heapProfBuf.Reset()
return nil
},
}
}
hooks.RegisterHook("heap", hf)
}
// RegisterProfCaptureHook registers a CaptureHookFactory for the
// supplied identifier. It panics if the same identifier is
// registered twice.
func RegisterProfCaptureHook(name string, c CaptureHookFactory) {
if _, exists := profCaptureHookRegistry[name]; exists {
panic(fmt.Sprintf("RegisterProfCaptureHook: %s registered twice", name))
}
profCaptureHookRegistry[name] = c
}
// EnableProfCaptureHook actives a registered profile capture hook for a given pipeline.
func EnableProfCaptureHook(name string, opts ...string) {
_, exists := profCaptureHookRegistry[name]
if !exists {
panic(fmt.Sprintf("EnableProfCaptureHook: %s not registered", name))
}
enc := hooks.Encode(name, opts)
for i, h := range enabledProfCaptureHooks {
n, _ := hooks.Decode(h)
if h == n {
// Rewrite the registration with the current arguments
enabledProfCaptureHooks[i] = encView on GitHub (pinned to 12126d8942)