{"record":{"id":"cc39e4292d8baf39","repo":"temporalio/temporal","slug":"component-type-s-is-already-registered","errorCode":null,"errorMessage":"component type %s is already registered","messagePattern":"component type (.+?) is already registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"chasm/registry.go","lineNumber":276,"sourceCode":"\n\tfor key, value := range rc.contextValues {\n\t\tif existingValue, ok := r.rcContextValues[key]; ok {\n\t\t\treturn fmt.Errorf(\"context value key %v registered by component %s conflicts with component %s\", key, fqn, existingValue.fqn)\n\t\t}\n\t\tr.rcContextValues[key] = valueWithFqn{\n\t\t\tv:   value,\n\t\t\tfqn: fqn,\n\t\t}\n\t}\n\n\t// rc.goType implements Component interface; therefore, it must be a struct.\n\t// This check to protect against the interface itself being registered.\n\tif !(rc.goType.Kind() == reflect.Struct ||\n\t\t(rc.goType.Kind() == reflect.Pointer && rc.goType.Elem().Kind() == reflect.Struct)) {\n\t\treturn fmt.Errorf(\"component type %s must be struct or pointer to struct\", rc.goType.String())\n\t}\n\tif _, ok := r.rcByGoType[rc.goType]; ok {\n\t\treturn fmt.Errorf(\"component type %s is already registered\", rc.goType.String())\n\t}\n\tr.warnUnmanagedFields(fqn, rc)\n\n\tr.rcByFqn[fqn] = rc\n\tr.rcByID[id] = rc\n\tr.rcByGoType[rc.goType] = rc\n\treturn nil\n}\n\nfunc (r *Registry) validate(rc *RegistrableComponent) error {\n\tif err := r.validateName(rc.componentType); err != nil {\n\t\treturn err\n\t}\n\treturn r.validateVisibilityBusinessIDAlias(rc)\n}\n\nfunc (r *Registry) registerTask(\n\tlib namer,","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/chasm/registry.go#L258-L294","documentation":"registerComponent detects that a component with the same Go type was already registered in this Registry (map r.rcByGoType). The registry keeps one entry per Go type, so duplicate registration would silently overwrite state and is rejected. Note this is keyed by Go type, distinct from the FQN/name collision error.","triggerScenarios":"Calling chasm.Register twice with the same component Go type, even if other options differ; calling Register in multiple init() functions or test setups that share one Registry; a shared library package whose init registers a component an app also registers.","commonSituations":"Duplicate init() in main package and imported package; test suite re-registering in a beforeEach; two binaries linking the same registration helper.","solutions":["Remove the duplicate chasm.Register call so the component is registered exactly once.","Guard registration with a sync.Once or package-level init in a single shared package.","If per-test registries are intended, construct a fresh Registry instead of reusing the global one."],"exampleFix":"// before\nfunc init() { chasm.Register[Foo](reg, opts) }\nfunc main() { chasm.Register[Foo](reg, opts) }\n// after\nfunc init() { chasm.Register[Foo](reg, opts) }","handlingStrategy":"validation","validationCode":"if _, ok := registryLookupComponent(reflect.TypeOf(MyComponentImpl{})); ok {\n\treturn errors.New(\"component already registered; skip duplicate Register call\")\n}","typeGuard":null,"tryCatchPattern":"if err := chasm.Register[MyComponentImpl](reg, opts); err != nil {\n\tvar dup *DuplicateRegistrationError\n\tif errors.As(err, &dup) { /* idempotent: skip */ }\n\treturn err\n}","preventionTips":["Register each component exactly once, in one init() function.","Use sync.Once around registration in shared helper packages.","Search the codebase for the component name before adding a new Register call."],"tags":["chasm","registry","duplicate-registration","component-registration"],"backgroundTag":"duplicate-component-registration","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}