{"record":{"id":"564294e48389f2e0","repo":"apache/beam","slug":"enablehook-can-t-enable-hook-s-hook-s-already-enabled","errorCode":null,"errorMessage":"EnableHook: can't enable hook %s, hook %s already enabled","messagePattern":"EnableHook: can't enable hook (.+?), hook (.+?) already enabled","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/util/grpcx/hook.go","lineNumber":82,"sourceCode":"\t\t\t},\n\t\t}\n\t}\n\thooks.RegisterHook(\"grpc\", hf)\n}\n\n// EnableHook is called to request the use of the gRPC\n// hook in a pipeline.\nfunc EnableHook(name string, opts ...string) {\n\t_, exists := hookRegistry[name]\n\tif !exists {\n\t\tpanic(fmt.Sprintf(\"EnableHook: %s not registered\", name))\n\t}\n\t// Only one hook can be enabled. If the pipeline has two conflicting views about how to use gRPC\n\t// that won't end well.\n\tif exists, opts := hooks.IsEnabled(\"grpc\"); exists {\n\t\tn, _ := hooks.Decode(opts[0])\n\t\tif n != name {\n\t\t\tpanic(fmt.Sprintf(\"EnableHook: can't enable hook %s, hook %s already enabled\", name, n))\n\t\t}\n\t}\n\n\thooks.EnableHook(\"grpc\", hooks.Encode(name, opts))\n}\n","sourceCodeStart":64,"sourceCodeEnd":88,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/util/grpcx/hook.go#L64-L88","documentation":"EnableHook allows only one gRPC hook to be active per process; if a different hook is already enabled it panics with a message naming both hooks. Conflicting hooks would produce incompatible gRPC behavior across the pipeline, so the conflict is treated as fatal.","triggerScenarios":"Calling grpcx.EnableHook(\"hookA\") after grpcx.EnableHook(\"hookB\") has already run (e.g. once from a library's init() and again from pipeline options processing) with different names.","commonSituations":"Two frameworks/libraries each enabling their own gRPC hook in the same binary; pipeline options being parsed twice with different hook settings; a test harness enabling a hook while the code under test enables another.","solutions":["Remove one of the conflicting EnableHook calls so only a single hook is enabled","Use the same hook name everywhere (EnableHook is idempotent for the same name)","Centralize hook enabling in one place (e.g. main) instead of library init() functions","Check hooks.IsEnabled(\"grpc\") and decode the active hook before enabling another"],"exampleFix":"// before\ngrpcx.EnableHook(\"custom\")\ngrpcx.EnableHook(\"default\") // panics: already enabled\n// after\nif enabled, opts := hooks.IsEnabled(\"grpc\"); !enabled {\n\tgrpcx.EnableHook(\"custom\")\n}","handlingStrategy":"try-catch","validationCode":"if enabled, opts := hooks.IsEnabled(\"grpc\"); enabled {\n\tif n, _ := hooks.Decode(opts[0]); n != desiredName {\n\t\treturn fmt.Errorf(\"hook %s already enabled; refusing to enable %s\", n, desiredName)\n\t}\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n\tif r := recover(); r != nil {\n\t\terr = fmt.Errorf(\"hook conflict: %v\", r)\n\t}\n}()","preventionTips":["Check hooks.IsEnabled before enabling a different hook","Enable hooks in exactly one place in the binary","Coordinate hook selection across libraries that both use grpcx"],"tags":["go","grpc","panic","configuration-conflict"],"backgroundTag":"conflicting-config-options","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"}