apache/beam · error

Logical Types must be registered with interface types. %v is

Error message

Logical Types must be registered with interface types. %v is not an interface type.

What it means

RegisterLogicalTypeProvider maps a LogicalTypeProvider to an interface type so providers can be matched against values whose static type is an interface. Passing a non-interface reflect.Type breaks that contract, so registration panics.

Source

Thrown at sdks/go/pkg/beam/core/runtime/graphx/schema/logicaltypes.go:109

func (r *Registry) RegisterLogicalType(lt LogicalType) {
	// Validates that the storage type has known handling.
	st := lt.StorageType()
	_, err := r.reflectTypeToFieldType(st)
	if err != nil {
		panic(fmt.Sprintf("LogicalType[%v] has an invalid StorageType %v: %v", lt.ID(), st, err))
	}
	if len(lt.ID()) == 0 {
		panic(fmt.Sprintf("invalid logical type, bad id: %v -> %v", lt.GoType(), lt.StorageType()))
	}
	// TODO add duplication checks.
	r.logicalTypeIdentifiers[lt.GoType()] = lt.ID()
	r.logicalTypes[lt.ID()] = lt
}

// RegisterLogicalTypeProvider allows registration of providers for interface types.
func (r *Registry) RegisterLogicalTypeProvider(rt reflect.Type, ltp LogicalTypeProvider) {
	if rt.Kind() != reflect.Interface {
		panic(fmt.Sprintf("Logical Types must be registered with interface types. %v is not an interface type.", rt))
	}
	if rt.NumMethod() == 0 {
		panic(fmt.Sprintf("Logical Types may not be registered with empty interface types. %v has no methods.", rt))
	}
	r.logicalTypeProviders[rt] = ltp
	r.logicalTypeInterfaces = append(r.logicalTypeInterfaces, rt)
}

// LogicalType is a mapping between custom Go types, and their schema equivalent storage types.
//
// A LogicalType is a way to define a type that can be stored in a schema field
// using a known underlying type for storage. The storage type must be comprised of
// known schema field types, or pre-registered LogicalTypes.
//
// LogicalTypes may not be mutually recursive at any level of indirection.
// LogicalTypes must map from a Go type to a single Schema Equivalent storage type.
type LogicalType struct {
	identifier          string

View on GitHub (pinned to 12126d8942)

Solutions

  1. Pass reflect.TypeOf((*MyInterface)(nil)).Elem() to obtain the interface type
  2. Use RegisterLogicalType instead for concrete Go types
  3. Verify rt.Kind() == reflect.Interface before calling

Example fix

// before
schema.RegisterLogicalTypeProvider(registry, reflect.TypeOf(MyImpl{}), provider)
// after
var iface MyInterface
schema.RegisterLogicalTypeProvider(registry, reflect.TypeOf(&iface).Elem(), provider)
Defensive patterns

Strategy: validation

Validate before calling

rt := reflect.TypeOf((*MyIface)(nil)).Elem()
if rt.Kind() != reflect.Interface { return errors.New("must pass an interface type") }
r.RegisterLogicalTypeProvider(rt, provider)

Type guard

func isInterfaceType(rt reflect.Type) bool { return rt != nil && rt.Kind() == reflect.Interface }

Try / catch

defer func() { if r := recover(); r != nil { err = fmt.Errorf("register provider: %v", r) } }()

Prevention

When it happens

Trigger: Calling Registry.RegisterLogicalTypeProvider with a concrete type (struct, pointer, basic type) as rt instead of a reflect.Type produced by reflect.TypeOf on an interface value.

Common situations: Accidentally registering the concrete implementation type instead of the interface; misunderstanding that the API requires interfaces with methods.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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