apache/beam · error
cannot make schema decoder for type
Error message
cannot make schema decoder for type %v as it has unexported fields such as %s.
What it means
The struct type has unexported fields and the decoder options set RequireAllFieldsExported, so the reflective schema decoder refuses to be built rather than silently dropping data. Without the flag the SDK silently adds a no-op decoder for such fields, meaning data loss; with the flag it fails fast.
Solutions
- Export the offending field (rename, e.g. `cached` → `Cached`) if it is meaningful data.
- Remove fields that are not part of the logical row (e.g. sync.Mutex) from the encoded struct.
- Disable RequireAllFieldsExported if silently ignoring unexported fields is acceptable.
- Provide a custom coder for the type.
Example fix
// before
type Record struct {
ID string
cache string // unexported
}
// after
type Record struct {
ID string
Cache string
} Defensive patterns
Strategy: validation
Validate before calling
func requireExportedFields(t reflect.Type) error {
for i := 0; i < t.NumField(); i++ {
if !t.Field(i).IsExported() {
return fmt.Errorf("type %s has unexported field %s", t, t.Field(i).Name)
}
}
return nil
} Try / catch
if err := requireExportedFields(reflect.TypeOf(myRecord{})); err != nil {
return fmt.Errorf("record not row-encodable: %w", err)
} Prevention
- Audit structs used in schema coding for unexported fields (mutexes, caches).
- Keep RequireAllFieldsExported enabled in CI tests to fail fast at compile/deploy time.
- Prefer exported field names for all persisted data.
- Use custom coders for types that must keep unexported internals.
When it happens
Trigger: decoderForStructReflect (via decoderForType) on a struct with any lowercase/unexported field (e.g. `mu sync.Mutex` or `cached string`) while DecoderOptions.RequireAllFieldsExported is true — typically set via RowCoder construction or schema registration with strict options.
Common situations: Users enable RequireAllFieldsExported to avoid silent data loss, then hit it on types with mutexes, sync primitives, or cache fields; common when upgrading pipelines that previously used lenient decoding.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/a050b5da227edcb7.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/core/graph/coder/row_decoder.go:160
// 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
}})
continue
}
dec, err := b.decoderForSingleTypeReflect(sf.Type)
if err != nil {
return nil, err
}
coder.fields = append(coder.fields, dec)
}
return func(rv reflect.Value, r io.Reader) error {
nf, nils, err := ReadRowHeader(r)
if err != nil {
return errView on GitHub (pinned to 12126d8942)