{"record":{"id":"7cd876b3fb3d26e0","repo":"apache/beam","slug":"unable-to-get-row-encoder","errorCode":null,"errorMessage":"unable to get row encoder","messagePattern":"unable to get row encoder","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/io/xlang/jdbcio/jdbc.go","lineNumber":109,"sourceCode":"\tWriteStatement        *string   `beam:\"writeStatement\"`\n\tFetchSize             *int16    `beam:\"fetchSize\"`\n\tOutputParallelization *bool     `beam:\"outputParallelization\"`\n}\n\n// jdbcConfig stores the expansion service and configuration for JDBC IO.\ntype jdbcConfig struct {\n\tclasspaths    []string\n\texpansionAddr string\n\tconfig        *config\n}\n\n// TODO(riteshghorse): update the IO to use wrapper created in BigQueryIO.\nfunc toRow(pl any) []byte {\n\trt := reflect.TypeOf(pl)\n\n\tenc, err := coder.RowEncoderForStruct(rt)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"unable to get row encoder\"))\n\t}\n\tvar buf bytes.Buffer\n\tif err := enc(pl, &buf); err != nil {\n\t\tpanic(fmt.Errorf(\"unable to do row encoding\"))\n\t}\n\treturn buf.Bytes()\n}\n\n// Write is a cross-language PTransform which writes Rows to the specified database via JDBC.\n// tableName is a required parameter, and by default, the write statement is generated from it.\n// The generated write statement can be overridden by passing in a WriteStatement option.\n// If an expansion service address is not provided,\n// an appropriate expansion service will be automatically started; however\n// this is slower than having a persistent expansion service running.\n//\n// If no additional classpaths are provided using jdbcio.WriteClasspaths() then the default classpath\n// for that driver would be used. As of now, the default classpaths are present only for PostgreSQL and MySQL.\n//","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/io/xlang/jdbcio/jdbc.go#L91-L127","documentation":"jdbcio's toRow converts a typed struct into an encoded Beam row payload. It first asks the coder package for a RowEncoderForStruct for the element's reflect.Type; when that fails (the type cannot be encoded as a Beam row), it panics with 'unable to get row encoder'. This is a panic, so it aborts the bundle rather than being a returned error.","triggerScenarios":"Using xlang/jdbcio.Read or jdbcio.Write with a Go type that coder.RowEncoderForStruct cannot handle: non-struct types (map, interface, primitive), structs with unsupported field types, or unexported/complex fields the row encoder doesn't support.","commonSituations":"Passing *map[string]interface{} or a custom type alias as the row type; a struct containing unsupported types (e.g. chan, func, nested unregistered types); registering a custom type that lacks required schema mapping.","solutions":["Use a plain exported Go struct with types supported by Beam's schema encoding (strings, ints, floats, bools, time.Time, nested structs of the same).","Check the underlying error by temporarily replacing the panic or reviewing RowEncoderForStruct docs to see which field is unsupported.","Flatten or convert unsupported fields (e.g. store JSON as string) before the JDBC transform.","Ensure pointer vs value type consistency: register the same concrete struct type you pass at runtime (TypeOf(pl) is taken from the actual value)."],"exampleFix":"// before\nrows := p | beam.Create(ctx, []map[string]interface{}{...})\nrows | jdbcio.Write(s, jdbcio.IO{...}) // panic: unable to get row encoder\n\n// after\ntype User struct {\n    ID    int64\n    Name  string\n    Email string\n}\nrows := p | beam.Create(ctx, []User{{ID: 1, Name: \"a\", Email: \"a@x.com\"}})\nrows | jdbcio.Write(s, jdbcio.IO{...})","handlingStrategy":"type-guard","validationCode":"func isJdbcRowType(v interface{}) bool {\n    t := reflect.TypeOf(v)\n    if t == nil || t.Kind() != reflect.Struct {\n        return false\n    }\n    _, err := coder.RowEncoderForStruct(t)\n    return err == nil\n}","typeGuard":"// Only pass exported structs with supported field types to jdbcio:\nfunc assertStructRow(v any) {\n    t := reflect.TypeOf(v)\n    if t.Kind() != reflect.Struct {\n        panic(\"jdbcio requires a struct element type, got \" + t.Kind().String())\n    }\n    if _, err := coder.RowEncoderForStruct(t); err != nil {\n        panic(\"type not encodable as Beam row: \" + err.Error())\n    }\n}","tryCatchPattern":null,"preventionTips":["Define plain exported structs with Beam-supported field types for JDBC IO.","Avoid map/interface/primitive element types for xlang jdbcio transforms.","Add a pipeline-construction test that calls RowEncoderForStruct on your row type.","Avoid func/chan fields in row structs."],"tags":["beam-go","jdbc","encoding","reflection","panic"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}