apache/beam · error

Nested type in struct

Error message

Nested type in struct "%v" not permitted in concrete types.

What it means

When validating a struct type, isConcrete recurses into each exported (upper-case initial) field; if any field's type fails validation, the error is wrapped with 'Nested type in struct ...' naming the containing struct. Private fields are deliberately ignored, so only exported fields can trigger this.

Solutions

  1. Remove or unexport the offending field (unexported fields are skipped by validation).
  2. Keep context and errors out of element types: handle errors via side outputs, metrics, or logging inside DoFns.
  3. Split transport-safe records from runtime-only state in separate types.
  4. Call typex.CheckConcrete(reflect.TypeOf(record)) in tests to catch offending fields early.

Example fix

// before
type Record struct {
    Payload []byte
    Ctx     context.Context
}

// after
type Record struct {
    Payload []byte
}
Defensive patterns

Strategy: validation

Validate before calling

if err := typex.CheckConcrete(reflect.TypeOf(record)); err != nil {
    return fmt.Errorf("struct element type rejected: %w", err)
}

Type guard

func structFieldsSafe(t reflect.Type) bool {
    if t.Kind() != reflect.Struct {
        return true
    }
    for i := 0; i < t.NumField(); i++ {
        if t.Field(i).PkgPath != "" {
            continue // unexported
        }
        switch t.Field(i).Type.Kind() {
        case reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Uintptr:
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: Using an element type struct with an exported chan, func, unsafe.Pointer, uintptr, or context/error-typed field — e.g. struct{ Data []byte; Cancel context.Context } or struct{ Err error }.

Common situations: Carrying context.Context or error in event records; structs that embed a mutex plus a channel for signaling; records that double as application objects with callback hooks.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/92a147fc12694fe4. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/core/typex/class.go:167

		if err != nil {
			err = errors.Wrapf(err, "Nested type in %v \"%v\" not permitted in concrete types.", t.Kind(), t)
		}
		return err

	case reflect.Struct:
		for i := 0; i < t.NumField(); i++ {
			// We ignore private fields under the assumption that they are
			// either not needed or will be coded manually. For combiner
			// accumulators, we need types that need non-trivial coding. Also,
			// Go serialization schemes in general ignore private fields.

			f := t.Field(i)
			if len(f.Name) > 0 {
				r, _ := utf8.DecodeRuneInString(f.Name)
				if unicode.IsUpper(r) {
					err := isConcrete(f.Type, visited)
					if err != nil {
						return errors.Wrapf(err, "Nested type in struct \"%v\" not permitted in concrete types.", t)
					}
				}
			}
		}
		return nil

	case reflect.Interface:
		// Interface types must fail at construction time if no coder is registered for them.
		return nil

	case reflect.Bool:
		return nil

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return nil

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return nil

View on GitHub (pinned to 12126d8942)