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

  1. Implement ID() to return a unique, stable non-empty string identifier
  2. Add a guard in your LogicalType constructor to fail early on empty IDs
  3. 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

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


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