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

  1. Change the embedded field to an exported type (or non-pointer embedded struct with exported fields).
  2. 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.
  3. If the embedded unexported type is a struct and not needed, embed its exported base type instead.
  4. 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

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


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)