apache/beam · error

interface type must have methods

Error message

interface type %v must have methods

What it means

RowDecoderBuilder.Register panics when rt is the empty interface type (interface{}) with no methods. An empty interface cannot be decoded because the SDK cannot determine a concrete representation or schema for it, so registering a decoder factory for it is rejected.

Solutions

  1. Pass the concrete struct type instead of interface{} (e.g. reflect.TypeOf(Foo{}))
  2. In generic code, capture the concrete type via a typed parameter or type switch before calling Register

Example fix

// before
b.Register(reflect.TypeOf((*any)(nil)).Elem(), decFn)
// after
b.Register(reflect.TypeOf(Foo{}), decFn)
Defensive patterns

Strategy: validation

Validate before calling

if rt.Kind() == reflect.Interface && rt.NumMethod() == 0 {
	return fmt.Errorf("cannot register decoder for empty interface %v", rt)
}

Type guard

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

Try / catch

func safeRegister(b *rowcoder.RowDecoderBuilder, rt reflect.Type, f any) (err error) {
	defer func() { if r := recover(); r != nil { err = fmt.Errorf("register: %v", r) } }()
	b.Register(rt, f)
	return nil
}

Prevention

When it happens

Trigger: Calling b.Register(reflect.TypeOf((*any)(nil)).Elem(), decoderFn) or otherwise passing reflect.TypeOf of an empty interface type while registering row decoders.

Common situations: Generic helper code that stores the element type as any/interface{} and passes it through to Register without narrowing to the concrete element type.

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/d187e550c640988b. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/core/graph/coder/row_decoder.go:57

// Register accepts a provider to decode schema encoded values
// of that type.
//
// When decoding values, decoder functions produced by this builder will
// first check for exact type matches, then interfaces implemented by
// the type in recency order of registration, and then finally the
// default Beam Schema encoding behavior.
//
// TODO(BEAM-9615): Add final factory types. This interface is subject to change.
// Currently f must be a function  func(reflect.Type) (func(io.Reader) (any, error), error)
func (b *RowDecoderBuilder) Register(rt reflect.Type, f any) {
	fd, ok := f.(decoderProvider)
	if !ok {
		panic(fmt.Sprintf("%T isn't a supported decoder function type (passed with %v), currently expecting %T", f, rt, (decoderProvider)(nil)))
	}

	if rt.Kind() == reflect.Interface && rt.NumMethod() == 0 {
		panic(fmt.Sprintf("interface type %v must have methods", rt))
	}

	if b.allFuncs == nil {
		b.allFuncs = make(map[reflect.Type]decoderProvider)
	}
	b.allFuncs[rt] = fd
	if rt.Kind() == reflect.Interface {
		b.ifaceFuncs = append(b.ifaceFuncs, rt)
	}
}

// Build constructs a Beam Schema coder for the given type, using any providers registered for
// itself or it's fields.
func (b *RowDecoderBuilder) Build(rt reflect.Type) (func(io.Reader) (any, error), error) {
	if err := rowTypeValidation(rt, true); err != nil {
		return nil, err
	}
	return b.decoderForType(rt)

View on GitHub (pinned to 12126d8942)