apache/beam · error
cannot make schema decoder for type
Error message
cannot make schema decoder for type %v as it has an embedded field of a pointer to an unexported type %v. See https://golang.org/issue/21357
What it means
The Beam Go SDK cannot build a schema row decoder for a struct type because one of its fields is embedded (anonymous) and is a pointer to an unexported type. Reflectively creating instances of such types panics in Go (golang.org/issue/21357), so the SDK refuses up-front instead of failing at decode time. Since values are created via reflection in this package, there is no workaround like pre-allocating the field.
Solutions
- Change the embedded field to an exported type (or non-pointer embedded struct with exported fields).
- Remove the embedded pointer field and hold it as a named, non-encoded field outside the schema type, or use a wrapper struct for encoding.
- If the embedded unexported type is a struct and not needed, embed its exported base type instead.
- Wrap the type with a custom coder (beam.CustomCoder) so reflective schema decoding is never used.
Example fix
// before
type Outer struct {
*innerConfig // pointer to unexported type
Name string
}
// after
type InnerConfig struct{ Opt string }
type Outer struct {
InnerConfig
Name string
} Defensive patterns
Strategy: validation
Validate before calling
func validateSchemaEncodable(t reflect.Type) error {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous && f.Type.Kind() == reflect.Ptr {
if isUnexportedType(f.Type.Elem()) {
return fmt.Errorf("type %s embeds pointer to unexported type %s", t, f.Type.Elem())
}
}
}
return nil
}
func isUnexportedType(t reflect.Type) bool { return !ast.IsExported(t.Name()) } Prevention
- Only embed exported types (or non-pointer exported structs) in schema-coded structs.
- Keep mutexes, sync primitives and caches out of encoded structs.
- Register a custom coder for types with unexported parts.
- Run a test that builds the RowCoder for every schema type at startup.
When it happens
Trigger: Calling decoderForType / decoderForSingleTypeReflect (e.g. via beam schema registration or RowCoder creation) on a struct with a field declared like `*unexportedType` as an embedded pointer field.
Common situations: Users register custom Go structs with beam.RegisterSchemaType (or use them in PCollection row encoding) where a struct embeds a pointer to a package-private type, often generated code or a mutex-like helper type.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- cannot make schema decoder for type
- A method marked with SchemaCreate in class
- AutoValue builder class
- bad decode
- bad encode
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/e95615c2ae4e103d.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/core/graph/coder/row_decoder.go:148
coder.typ = t
for i := 0; i < t.NumField(); i++ {
i := i // avoid alias issues in the closures.
sf := t.Field(i)
isUnexported := sf.PkgPath != ""
if sf.Anonymous {
ft := sf.Type
if ft.Kind() == reflect.Ptr {
// If a struct embeds a pointer to an unexported type,
// it is not possible to set a newly allocated value
// since the field is unexported.
//
// See https://golang.org/issue/21357
//
// Since the values are created by this package reflectively,
// there's no work around like pre-allocating the field
// manually.
if isUnexported {
return nil, errors.Errorf("cannot make schema decoder for type %v as it has an embedded field of a pointer to an unexported type %v. See https://golang.org/issue/21357", t, ft.Elem())
}
ft = ft.Elem()
}
if isUnexported && ft.Kind() != reflect.Struct {
// Ignore embedded fields of unexported non-struct types.
continue
}
// Do not ignore embedded fields of unexported struct types
// since they may have exported fields.
} else if isUnexported {
if b.RequireAllFieldsExported {
return nil, errors.Errorf("cannot make schema decoder for type %v as it has unexported fields such as %s.", t, sf.Name)
}
// Silently ignore, since we can't do anything about it.
// Add a no-op coder to fill in field index
coder.fields = append(coder.fields, typeDecoderFieldReflect{decode: func(rv reflect.Value, r io.Reader) error {
return nil
}})View on GitHub (pinned to 12126d8942)