apache/beam · critical

type already registered for

Error message

type already registered for %v, and new type %v != %v (existing type)

What it means

This panic fires in RegisterType when two different Go types map to the same TypeKey. The Apache Beam Go SDK keeps a global registry mapping string type keys to reflect.Type values; if a key computed for a new type is already present but bound to a different type, the registry would be corrupted, so the SDK panics. It signals a genuine type-identity collision, not a user-facing error return.

Solutions

  1. Check which two types collide: the panic message shows the key, the new type, and the existing type; align the registrations so only one type is registered per key.
  2. Remove duplicate beam.RegisterType / RegisterDoFn / RegisterCoder calls for the colliding type (often in init functions of copied or vendored packages).
  3. If a custom type key is being generated, make it unique (include the full package path and any generic parameters).
  4. Restructure code so distinct types are not name-identical across vendor copies; consolidate duplicated package copies.

Example fix

// before
// package a: type Event struct{}
// package b (vendored copy): type Event struct{}
beam.RegisterType(reflect.TypeOf(a.Event{}))
beam.RegisterType(reflect.TypeOf(b.Event{})) // panics: same key, different type
// after
// keep a single canonical definition of Event and register it once
beam.RegisterType(reflect.TypeOf(a.Event{}))
Defensive patterns

Strategy: validation

Validate before calling

k := beam.TypeKey(reflect.TypeOf(myType{}))
if _, exists := registryCheck(k); exists { /* skip duplicate registration or ensure identical type */ }

Prevention

When it happens

Trigger: Calling beam.RegisterType (directly or via RegisterDoFn/RegisterCoder) with a type whose TypeKey (type name incl. package path, plus generic type arguments) collides with an already-registered but distinct type; e.g. registering two different types that produce the same key, or registering a type in a package that gets re-loaded under an identical key.

Common situations: Duplicate registrations across packages with identical type names in different vendored copies; custom TypeKey/Encoders registered twice with differing types; build-tag or plugin setups that load the same package twice under one binary; renaming a struct while stale generated init code still registers the old one.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/1d72d503c149cfd1. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/core/runtime/types.go:43

var types = make(map[string]reflect.Type)

// RegisterType inserts "external" types into a global type registry to bypass
// serialization and preserve full method information. It should be called in
// init() only. Returns the external key for the type.
func RegisterType(t reflect.Type) string {
	if initialized {
		panic("Init hooks have already run. Register type during init() instead.")
	}

	t = reflectx.SkipPtr(t)

	k, ok := TypeKey(t)
	if !ok {
		panic(fmt.Sprintf("invalid registration type: %v", t))
	}

	if v, exists := types[k]; exists && v != t {
		panic(fmt.Sprintf("type already registered for %v, and new type %v != %v (existing type)", k, t, v))
	}
	types[k] = t
	return k
}

// LookupType looks up a type in the global type registry by external key.
func LookupType(key string) (reflect.Type, bool) {
	t, ok := types[key]
	return t, ok
}

// TypeKey returns the external key of a given type. Returns false if not a
// candidate for registration.
func TypeKey(t reflect.Type) (string, bool) {
	if t.PkgPath() == "" || t.Name() == "" {
		return "", false // no pre-declared or unnamed types
	}
	return fmt.Sprintf("%v.%v", t.PkgPath(), t.Name()), true

View on GitHub (pinned to 12126d8942)