{"record":{"id":"181e8050adfb1a7a","repo":"apache/beam","slug":"interface-type-v-must-have-methods-row-encoder","errorCode":null,"errorMessage":"interface type %v must have methods","messagePattern":"interface type (.+?) must have methods","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/graph/coder/row_encoder.go","lineNumber":55,"sourceCode":"\ntype encoderProvider = func(reflect.Type) (func(any, io.Writer) error, error)\n\n// Register accepts a provider for the given type to schema encode values of that type.\n//\n// When generating encoding functions, this builder will first check for exact type\n// matches, then against interfaces with registered factories in recency order of\n// registration, and then finally use the default Beam Schema encoding behavior.\n//\n// TODO(BEAM-9615): Add final factory types. This interface is subject to change.\n// Currently f must be a function of the type func(reflect.Type) func(T, io.Writer) (error).\nfunc (b *RowEncoderBuilder) Register(rt reflect.Type, f any) {\n\tfe, ok := f.(encoderProvider)\n\tif !ok {\n\t\tpanic(fmt.Sprintf(\"%T isn't a supported encoder function type (passed with %v)\", f, rt))\n\t}\n\n\tif rt.Kind() == reflect.Interface && rt.NumMethod() == 0 {\n\t\tpanic(fmt.Sprintf(\"interface type %v must have methods\", rt))\n\t}\n\tif b.allFuncs == nil {\n\t\tb.allFuncs = make(map[reflect.Type]encoderProvider)\n\t}\n\tb.allFuncs[rt] = fe\n\tif rt.Kind() == reflect.Interface {\n\t\tb.ifaceFuncs = append(b.ifaceFuncs, rt)\n\t}\n}\n\n// Build constructs a Beam Schema coder for the given type, using any providers registered for\n// itself or it's fields.\nfunc (b *RowEncoderBuilder) Build(rt reflect.Type) (func(any, io.Writer) error, error) {\n\tif err := rowTypeValidation(rt, true); err != nil {\n\t\treturn nil, err\n\t}\n\treturn b.encoderForType(rt)\n}","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/graph/coder/row_encoder.go#L37-L73","documentation":"RowEncoderBuilder.Register panics when rt is the empty interface (interface{}) with no methods. An empty interface has no schema or concrete representation, so the SDK cannot attach a custom row encoder to it and rejects the registration.","triggerScenarios":"Calling b.Register(reflect.TypeOf((*any)(nil)).Elem(), encFn) or passing any zero-method interface type as rt while registering row encoders.","commonSituations":"Generic pipeline helpers that thread element types as interface{} and pass them straight through to Register without resolving to the concrete type.","solutions":["Register the concrete struct type (e.g. reflect.TypeOf(Foo{})) instead of interface{}","Resolve the concrete type with a type switch or typed generic parameter before calling Register"],"exampleFix":"// before\nb.Register(reflect.TypeOf((*any)(nil)).Elem(), encFn)\n// after\nb.Register(reflect.TypeOf(Foo{}), encFn)","handlingStrategy":"validation","validationCode":"if rt.Kind() == reflect.Interface && rt.NumMethod() == 0 {\n\treturn fmt.Errorf(\"cannot register encoder for empty interface %v\", rt)\n}","typeGuard":"func isRegisterableType(rt reflect.Type) bool {\n\treturn rt != nil && !(rt.Kind() == reflect.Interface && rt.NumMethod() == 0)\n}","tryCatchPattern":"func safeRegister(b *rowcoder.RowEncoderBuilder, rt reflect.Type, f any) (err error) {\n\tdefer func() { if r := recover(); r != nil { err = fmt.Errorf(\"register: %v\", r) } }()\n\tb.Register(rt, f)\n\treturn nil\n}","preventionTips":["Register concrete types only; never interface{}","Resolve generic/any element types with a type switch before Register"],"tags":["go","beam","reflection","interface"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T08:17:22.691Z"}