{"record":{"id":"08902bc6bdfc0b23","repo":"apache/beam","slug":"grpc-hook-s-registered-twice","errorCode":null,"errorMessage":"grpc.Hook: %s registered twice","messagePattern":"grpc\\.Hook: (.+?) registered twice","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/util/grpcx/hook.go","lineNumber":47,"sourceCode":"// is optional; the default behavior will be used if a value is not\n// supplied.\ntype Hook struct {\n\t// Dialer allows the runner to customize the gRPC dialing behavior.\n\tDialer func(context.Context, string, time.Duration) (*grpc.ClientConn, error)\n\t// TODO(wcn): expose other hooks here.\n}\n\n// HookFactory is a function that creates hooks from supplied arguments.\ntype HookFactory func([]string) Hook\n\nvar hookRegistry = make(map[string]HookFactory)\n\n// RegisterHook registers a HookFactory for the\n// supplied identifier. It panics if the same identifier is\n// registered twice.\nfunc RegisterHook(name string, c HookFactory) {\n\tif _, exists := hookRegistry[name]; exists {\n\t\tpanic(fmt.Sprintf(\"grpc.Hook: %s registered twice\", name))\n\t}\n\thookRegistry[name] = c\n\n\thf := func(opts []string) hooks.Hook {\n\t\treturn hooks.Hook{\n\t\t\tInit: func(ctx context.Context) (context.Context, error) {\n\t\t\t\tif len(opts) == 0 {\n\t\t\t\t\treturn ctx, nil\n\t\t\t\t}\n\n\t\t\t\tname, opts := hooks.Decode(opts[0])\n\t\t\t\tgrpcHook := hookRegistry[name](opts)\n\t\t\t\tif grpcHook.Dialer != nil {\n\t\t\t\t\tDial = grpcHook.Dialer\n\t\t\t\t}\n\t\t\t\treturn ctx, nil\n\t\t\t},\n\t\t}","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/util/grpcx/hook.go#L29-L65","documentation":"RegisterHook stores a gRPC HookFactory by identifier in a global registry and panics if the same identifier is already registered, enforcing that each hook name maps to exactly one factory. This is an init-time invariant check for duplicate hook definitions.","triggerScenarios":"Calling grpcx.RegisterHook(\"grpc\", factory) twice — e.g. the same hook package imported from two paths, or two packages both registering a hook with the identical name in init().","commonSituations":"Duplicate registration in init() functions across vendored and non-vendored copies of a hook package; two custom hooks accidentally given the same name; test binaries linking both a hook and its test double.","solutions":["Remove the duplicate RegisterHook call or guard it with a sync.Once","Rename one of the conflicting hooks so identifiers are unique","Check init() functions of imported packages for duplicate hook registrations","If intentional re-registration is needed, add an idempotency check before calling RegisterHook"],"exampleFix":"// before\nfunc init() { grpcx.RegisterHook(\"grpc\", myFactory) } // panics if already registered\n// after\nvar once sync.Once\nfunc init() { once.Do(func() { grpcx.RegisterHook(\"grpc\", myFactory) }) }","handlingStrategy":"try-catch","validationCode":"if _, exists := hookNameTaken(name); exists {\n\treturn fmt.Errorf(\"hook %s already registered\", name)\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n\tif r := recover(); r != nil {\n\t\terr = fmt.Errorf(\"hook registration failed: %v\", r)\n\t}\n}()","preventionTips":["Register hooks in a single init() guarded by sync.Once","Use unique, package-qualified hook names","Check import graphs for duplicate copies of the same hook package"],"tags":["go","grpc","panic","initialization"],"backgroundTag":"duplicate-registration-panic","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}