apache/beam · error
invalid logical type, bad id: %v -> %v
Error message
invalid logical type, bad id: %v -> %v
What it means
RegisterLogicalType requires a non-empty logical type identifier (lt.ID()), which is used as the schema protocol name for the type. An empty ID would make the type unreferenceable in the wire schema, so registration panics.
Source
Thrown at sdks/go/pkg/beam/core/runtime/graphx/schema/logicaltypes.go:99
idToType: map[string]reflect.Type{},
syntheticToUser: map[reflect.Type]reflect.Type{},
logicalTypes: map[string]LogicalType{},
logicalTypeIdentifiers: map[reflect.Type]string{},
logicalTypeProviders: map[reflect.Type]LogicalTypeProvider{},
}
}
// RegisterLogicalType a single logical type.
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)
}
View on GitHub (pinned to 12126d8942)
Solutions
- Implement ID() to return a unique, stable non-empty string identifier
- Add a guard in your LogicalType constructor to fail early on empty IDs
- Check for refactoring mistakes where the ID constant became an empty string
Example fix
// before
func (t myLT) ID() string { return "" }
// after
func (t myLT) ID() string { return "mycompany.myLogicalType" } Defensive patterns
Strategy: validation
Validate before calling
if len(lt.ID()) == 0 { return errors.New("logical type ID must be non-empty") }
r.RegisterLogicalType(lt) Try / catch
defer func() { if r := recover(); r != nil { err = fmt.Errorf("register logical type: %v", r) } }() Prevention
- Use reverse-DNS style stable identifiers
- Add constructor checks rejecting empty IDs
- Unit test ID() output
When it happens
Trigger: Registering a LogicalType whose ID() method returns an empty string, via RegisterLogicalType, registerType, or preRegLogicalTypes.
Common situations: Custom LogicalType implementations with placeholder or unimplemented ID() methods; empty constant identifiers after refactoring.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- LogicalType[%v] has an invalid StorageType %v: %v
- unable to provide schema.LogicalType for type %v, want %v
- reconciling for ToType
- Logical Types must be registered with interface types. %v is
- Logical Types may not be registered with empty interface typ
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/bff8549f0035595f.
Report an issue: GitHub.