apache/beam · warning

bad channel direction

Error message

bad channel direction

What it means

encodeType encodes channel types together with their direction via encodeChanDir; 'bad channel direction' means the channel's reflect.ChanDir could not be mapped to a protobuf ChanDir value. This is an internal mapping failure - the SDK's encodeChanDir only covers RecvDir, SendDir, and BothDir, so it should be unreachable for valid reflect types and signals an internal invariant violation or corrupted type metadata.

Solutions

  1. Report/verify against the Beam SDK version; upgrade to the latest patch release.
  2. Avoid channel types in serialized pipeline graphs entirely so this branch is never hit.
  3. Check whether a custom type wrapper is producing an invalid reflect.Type and fix it.
  4. File an issue with apache/beam including the wrapped error context ('encoding channel %v').
Defensive patterns

Strategy: try-catch

Try / catch

if _, err := graphx.EncodeFn(fn); err != nil && strings.Contains(err.Error(), "bad channel direction") {
    return fmt.Errorf("likely Beam SDK bug; report with context: %w", err)
}

Prevention

When it happens

Trigger: encodeType's reflect.Chan branch calls encodeChanDir with a direction outside the three valid reflect.ChanDir values - practically only via corrupted reflect metadata or an SDK-internal bug; triggered during pipeline graph export of a type containing a channel.

Common situations: Extremely rare; encountered when hacking reflect metadata, using unusual reflect constructs, or hitting a genuine Beam SDK bug while serializing channel-bearing types on remote runner export.

Related errors


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

Appendix: source

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

		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

	case reflect.Ptr:
		elm, err := encodeType(t.Elem())
		if err != nil {
			wrapped := errors.Wrap(err, "bad base type")
			return nil, errors.WithContextf(wrapped, "encoding pointer %v", t)
		}
		return &v1pb.Type{Kind: v1pb.Type_PTR, Element: elm}, nil

	case reflect.Map, reflect.Array:
		return nil, errors.Errorf("unencodable type '%v', try to wrap the type as a field in a struct, see https://github.com/apache/beam/issues/23101 for details", t.Kind())

	default:
		return nil, errors.Errorf("unencodable type '%v'", t.Kind())
	}

View on GitHub (pinned to 12126d8942)