apache/beam · error

cannot make schema encoder for type %v as it has an embedded

Error message

cannot make schema encoder 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

Beam cannot build a schema encoder for a struct with an embedded pointer to an unexported type, because reflect cannot safely construct that embedded value (golang.org/issue/21357). The encoder refuses rather than produce broken encodings.

Source

Thrown at sdks/go/pkg/beam/core/graph/coder/row_encoder.go:306

	var coder typeEncoderReflect
	for i := 0; i < t.NumField(); i++ {
		coder.debug = append(coder.debug, t.Field(i).Name+" "+t.Field(i).Type.String())
		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 encoder 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 encoder 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, typeEncoderFieldReflect{encode: func(rv reflect.Value, w io.Writer) error {
				return nil
			}})

View on GitHub (pinned to 12126d8942)

Solutions

  1. Remove the embedded pointer to the unexported type; embed an exported type instead
  2. Flatten the embedded fields into the struct explicitly
  3. Use a custom coder instead of reflective schema coding for this type

Example fix

// before
type Wrapper struct { *base }
// after
type Wrapper struct { Base } // where Base is exported
Defensive patterns

Strategy: validation

Validate before calling

func hasEmbeddedUnexportedPtr(t reflect.Type) bool {
    if t.Kind() != reflect.Struct { return false }
    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        if f.Anonymous && f.Type.Kind() == reflect.Ptr && !f.Type.Elem().IsExported() {
            return true
        }
    }
    return false
}

Type guard

func isSchemaEncodableStruct(t reflect.Type) bool { return !hasEmbeddedUnexportedPtr(t) }

Try / catch

if _, err := (coder.RowEncoderBuilder{}).Build(t); err != nil {
    return fmt.Errorf("struct %s not schema-encodable: %w", t, err)
}

Prevention

When it happens

Trigger: Encoding a struct that embeds *unexportedType, e.g. type T struct { *foo }; passing T (or a struct containing it) to reflective schema row encoding.

Common situations: Internal-ish embedded pointer types (common with generated or package-private base types) surfacing in PCollection elements.

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/27759a012b1f8163. Report an issue: GitHub.