apache/beam · error

bad return type

Error message

bad return type

What it means

encodeType serializes func types by encoding each return type; 'bad return type' means one of the function's output types failed to encode. Like the parameter-side error, it surfaces when exporting a DoFn or user function whose emit/output types cannot be represented on the wire.

Solutions

  1. Change the return/emitted type to an encodable form (slice of exported-field structs).
  2. Register the custom output type in the Beam type registry.
  3. Split emission into encodable sub-values (emit each map entry as a KV struct).
  4. Use the 'encoding function %v' context message to identify the failing signature.

Example fix

// before
func parse(line string) map[string]string { ... }
// after
type Field struct { K, V string }
func parse(line string) []Field { ... }
Defensive patterns

Strategy: validation

Validate before calling

ft := reflect.TypeOf(fn)
for i := 0; i < ft.NumOut(); i++ {
    if err := isEncodableType(ft.Out(i)); err != nil {
        return fmt.Errorf("return %d of %v unencodable: %w", i, ft, err)
    }
}

Type guard

func encodableReturns(fn any) bool { t := reflect.TypeOf(fn); if t.Kind() != reflect.Func { return false }; for i := 0; i < t.NumOut(); i++ { if isEncodableType(t.Out(i)) != nil { return false } }; return true }

Try / catch

if !encodableReturns(fn) {
    return fmt.Errorf("function %T returns unserializable types", fn)
}

Prevention

When it happens

Trigger: A user function returns (or emits via a func outlet parameter) a type that encodeType cannot encode: map/array returns, structs with unexported fields, or unregistered custom types.

Common situations: DoFns emitting map results; builders returning structs with private fields; pipelines exported for Flink/Dataflow/Spark runners.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/runtime/graphx/serialize.go:479

			fields = append(fields, field)
		}
		return &v1pb.Type{Kind: v1pb.Type_STRUCT, Fields: fields}, nil

	case reflect.Func:
		var in []*v1pb.Type
		for i := 0; i < t.NumIn(); i++ {
			param, err := encodeType(t.In(i))
			if err != nil {
				wrapped := errors.Wrap(err, "bad parameter type")
				return nil, errors.WithContextf(wrapped, "encoding function %v", t)
			}
			in = append(in, param)
		}
		var out []*v1pb.Type
		for i := 0; i < t.NumOut(); i++ {
			ret, err := encodeType(t.Out(i))
			if err != nil {
				wrapped := errors.Wrap(err, "bad return type")
				return nil, errors.WithContextf(wrapped, "encoding function %v", t)
			}
			out = append(out, ret)
		}
		return &v1pb.Type{Kind: v1pb.Type_FUNC, ParameterTypes: in, ReturnTypes: out, IsVariadic: t.IsVariadic()}, nil

	case reflect.Chan:
		elm, err := encodeType(t.Elem())
		if err != nil {
			wrapped := errors.Wrap(err, "bad element type")
			return nil, errors.WithContextf(wrapped, "encoding channel %v", t)
		}
		dir, err := encodeChanDir(t.ChanDir())
		if err != nil {
			wrapped := errors.Wrap(err, "bad channel direction")
			return nil, errors.WithContextf(wrapped, "encoding channel %v", t)
		}
		return &v1pb.Type{Kind: v1pb.Type_CHAN, Element: elm, ChanDir: dir}, nil

View on GitHub (pinned to 12126d8942)