apache/beam · error · IllegalStateException

Unknown type of CombineFn

Error message

Unknown type of CombineFn: %s

What it means

Combine.PerKeyWithHotKeyFanout wraps either a CombineFn or a KeyedCombineFn to split hot keys; when the typed function is neither, expand throws IllegalStateException reporting the unexpected CombineFn type.

Solutions

  1. Pass a CombineFn or KeyedCombineFn instance to withHotKeyFanout
  2. Check the class of the fn in the error message and align it with CombineFn/KeyedCombineFn
  3. Use Combine.perKey(fn).withHotKeyFanout(...) rather than composing custom wrapper types

Example fix

// before
.withHotKeyFanout(myRandomFunction) // not a CombineFn
// after
.withHotKeyFanout((CombineFn<InputT, AccumT, OutputT>) myCombineFn)
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(fn instanceof CombineFn) && !(fn instanceof KeyedCombineFn)) {
  throw new IllegalArgumentException("withHotKeyFanout requires CombineFn or KeyedCombineFn, got " + fn.getClass());
}

Type guard

boolean isSupportedCombineFn(Object fn) {
  return fn instanceof CombineFn || fn instanceof KeyedCombineFn;
}

Try / catch

try {
  return pc.apply(Combine.perKey(fn).withHotKeyFanout(hotKeyFn));
} catch (IllegalStateException e) {
  if (e.getMessage() != null && e.getMessage().contains("Unknown type of CombineFn")) {
    LOG.error("Wrap the fn as a CombineFn or KeyedCombineFn; got: " + e.getMessage(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling withHotKeyFanout(...) with a fn object that is neither a CombineFn nor a KeyedCombineFn (e.g. a wrongly-typed custom class or a lambda cast to an unexpected interface).

Common situations: Passing a SimpleFunction or other transform fn by mistake; refactors changing a fn's base class; generic type erasure letting the wrong class reach the else branch.

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


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

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/Combine.java:1910

                  CoderRegistry registry, Coder<InputOrAccum<InputT, AccumT>> accumulatorCoder)
                  throws CannotProvideCoderException {
                return fnWithContext.getDefaultOutputCoder(registry, inputCoder.getValueCoder());
              }

              @Override
              public Coder<AccumT> getAccumulatorCoder(
                  CoderRegistry registry, Coder<InputOrAccum<InputT, AccumT>> inputCoder)
                  throws CannotProvideCoderException {
                return accumCoder;
              }

              @Override
              public void populateDisplayData(DisplayData.Builder builder) {
                builder.delegate(PerKeyWithHotKeyFanout.this);
              }
            };
      } else {
        throw new IllegalStateException(
            String.format("Unknown type of CombineFn: %s", typedFn.getClass()));
      }

      // Use the provided hotKeyFanout fn to split into "hot" and "cold" keys,
      // augmenting the hot keys with a nonce.
      final TupleTag<KV<KV<K, Integer>, InputT>> hot = new TupleTag<>();
      final TupleTag<KV<K, InputT>> cold = new TupleTag<>();
      PCollectionTuple split =
          input.apply(
              "AddNonce",
              ParDo.of(
                      new DoFn<KV<K, InputT>, KV<K, InputT>>() {
                        transient int nonce;

                        @StartBundle
                        public void startBundle() {
                          // Spreading a hot key across all possible sub-keys for all bundles
                          // would defeat the goal of not overwhelming downstream reducers

View on GitHub (pinned to 12126d8942)