apache/beam · error
Logical Types may not be registered with empty interface typ
Error message
Logical Types may not be registered with empty interface types. %v has no methods.
What it means
A logical type provider registered on an interface with zero methods would match every Go value, since all types satisfy the empty interface. To prevent this pathological catch-all registration, the library panics when rt.NumMethod() == 0.
Source
Thrown at sdks/go/pkg/beam/core/runtime/graphx/schema/logicaltypes.go:112
_, 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
goT, storageT, argT reflect.Type
argV reflect.Value
}View on GitHub (pinned to 12126d8942)
Solutions
- Give the interface at least one method that your values implement
- Register concrete types via RegisterLogicalType instead of an empty-interface provider
- Add a marker method to the interface to narrow matching
Example fix
// before
type MyTag interface{}
// after
type MyTag interface{ IsMyType() } Defensive patterns
Strategy: validation
Validate before calling
if rt.Kind() == reflect.Interface && rt.NumMethod() == 0 {
return errors.New("provider interface must declare at least one method")
} Type guard
func isNonEmptyInterface(rt reflect.Type) bool { return rt != nil && rt.Kind() == reflect.Interface && rt.NumMethod() > 0 } Try / catch
defer func() { if r := recover(); r != nil { err = fmt.Errorf("register provider: %v", r) } }() Prevention
- Give marker interfaces at least one method
- Never register providers on interface{}
- Review interface design before registration
When it happens
Trigger: Calling RegisterLogicalTypeProvider with an interface type that declares no methods, e.g. reflect.TypeOf((*interface{})(nil)).Elem() or a marker interface without methods.
Common situations: Using interface{} or empty marker interfaces to register a provider; designing a tagged interface where methods were removed during refactoring.
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
- Logical Types must be registered with interface types. %v is
- panicked: %v
- err
- LogicalType[%v] has an invalid StorageType %v: %v
- invalid logical type, bad id: %v -> %v
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/e69d384c8d93b812.
Report an issue: GitHub.