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

  1. Export the offending field (rename, e.g. `cached` → `Cached`) if it is meaningful data.
  2. Remove fields that are not part of the logical row (e.g. sync.Mutex) from the encoded struct.
  3. Disable RequireAllFieldsExported if silently ignoring unexported fields is acceptable.
  4. 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

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 err

View on GitHub (pinned to 12126d8942)