apache/beam · error
unexpected aggregate
Error message
unexpected aggregate %v, only slices allowed
What it means
During typex.Substitute, an Aggregate type whose underlying Go type is not a slice cannot be reconstructed from its substituted components: only reflect.Slice types can be rebuilt with reflect.SliceOf. Any other aggregate kind triggers this error.
Solutions
- Convert the aggregate to a slice type (e.g. use []T instead of [N]T) in your Fn signature or FullType construction.
- If a map is intended, model it with typex.Container/map semantics rather than an Aggregate node.
- Avoid substituting types with free variables inside array aggregates — bind to a fully concrete type instead.
- Check the %v in the message for the offending aggregate type to confirm which declaration to change.
Example fix
// before typex.New(reflect.ArrayOf(2, typex.T.Type())) // array aggregate // after typex.New(reflect.SliceOf(typex.T.Type())) // slice aggregate
Defensive patterns
Strategy: type-guard
Validate before calling
func substitutableAggregate(t typex.FullType) bool {
return t.Class() != typex.Aggregate || typex.IsList(t.Type())
} Type guard
func isSliceAggregate(t typex.FullType) bool {
return t.Class() == typex.Aggregate && t.Type().Kind() == reflect.Slice
} Try / catch
out, err := typex.Substitute(t, m)
if err != nil && strings.Contains(err.Error(), "only slices allowed") {
return fmt.Errorf("use slice-based types for generic aggregates: %w", err)
} Prevention
- Prefer slice types ([]T) over fixed-size arrays ([N]T) in generic DoFn signatures.
- Do not embed type variables inside array or struct aggregates that will be substituted.
- Keep type variables in Container or Universal positions, which Substitute fully supports.
- If you need maps or structs with variables, bind them to concrete types first instead of substituting.
When it happens
Trigger: Substituting a FullType containing an Aggregate node whose t.Type() is an array, map, struct, or other non-slice composite, with variable components — e.g. an aggregate built from [2]T or map[string]T passed through Substitute.
Common situations: Users construct FullTypes with typex.New over array or map Go types containing type variables and then run Substitute; the Beam type system's substitution only supports slice-shaped aggregates, so pipelines using array-typed variables with generic coders hit this.
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
- substituting type : type not bound
- AfterProcessingTime trigger set without a delay or…
- array len mismatch. decoding
- At least one subtrigger required for composite triggers.
- attempted to add namespace to missing coder id
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/856c5f2e8a54fd9e.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/core/typex/fulltype.go:427
func substitute(t FullType, m map[string]reflect.Type) (FullType, error) {
switch t.Class() {
case Universal:
name := t.Type().Name()
repl, ok := m[name]
if !ok {
return nil, errors.Errorf("substituting type %v: type not bound", name)
}
return New(repl), nil
case Container:
comp, err := substituteList(t.Components(), m)
if err != nil {
return nil, err
}
if IsList(t.Type()) {
return New(reflect.SliceOf(comp[0].Type()), comp...), nil
}
return nil, errors.Errorf("unexpected aggregate %v, only slices allowed", t)
case Composite:
comp, err := substituteList(t.Components(), m)
if err != nil {
return nil, err
}
return New(t.Type(), comp...), nil
default:
return t, nil
}
}
func substituteList(list []FullType, m map[string]reflect.Type) ([]FullType, error) {
var ret []FullType
for _, elm := range list {
repl, err := substitute(elm, m)
if err != nil {
return nil, errView on GitHub (pinned to 12126d8942)