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

  1. Find the duplicate registration call sites (grep for RegisterProfCaptureHook with the offending name) and remove one.
  2. Register the hook only once — prefer package init in a single package, not both init and main.
  3. If two module versions of a hook package are linked, deduplicate the dependency (go mod tidy / go mod graph) so only one init runs.
  4. 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

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


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] = enc

View on GitHub (pinned to 12126d8942)