apache/beam · error · IllegalStateException

Subclass of class org.apache.beam.sdk.transforms.CombineFn m

Error message

Subclass of class org.apache.beam.sdk.transforms.CombineFn must be parameterized to be used as a UDAF

What it means

Beam SQL walks the UDAF's class hierarchy up to CombineFn and requires that the direct CombineFn superclass be a parameterized type, because the type parameters (InputT, AccumT, OutputT) are how SQL types are inferred. A subclass that extends CombineFn raw (or via a non-parameterized chain) gives no type information at runtime due to erasure, so the UDAF cannot be built.

Source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/UdafImpl.java:127

    }
    ParameterizedType parameterizedType = findCombineFnSuperClass();
    return parameterizedType.getActualTypeArguments()[0];
  }

  protected Type getOutputType() {
    return combineFn.getOutputType().getType();
  }

  private ParameterizedType findCombineFnSuperClass() {

    Class clazz = combineFn.getClass();

    while (!clazz.getSuperclass().equals(CombineFn.class)) {
      clazz = clazz.getSuperclass();
    }

    if (!(clazz.getGenericSuperclass() instanceof ParameterizedType)) {
      throw new IllegalStateException(
          "Subclass of " + CombineFn.class + " must be parameterized to be used as a UDAF");
    }
    return (ParameterizedType) clazz.getGenericSuperclass();
  }
}

View on GitHub (pinned to 12126d8942)

Solutions

  1. Parameterize the CombineFn subclass explicitly: class MySumFn extends CombineFn<Long, long[], Long> instead of raw extends CombineFn
  2. Ensure every class in the hierarchy between your UDAF and CombineFn propagates concrete type parameters
  3. Verify with clazz.getGenericSuperclass() instanceof ParameterizedType before registering

Example fix

// before
class MySumFn extends CombineFn { ... }
// after
class MySumFn extends CombineFn<Long, long[], Long> { ... }
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = myFn.getClass();
while (c != null && !c.getSuperclass().equals(CombineFn.class)) c = c.getSuperclass();
boolean ok = c != null && c.getGenericSuperclass() instanceof ParameterizedType;

Type guard

static boolean isParameterizedCombineFn(Class<?> clazz) {
  while (clazz != null && !clazz.getSuperclass().equals(CombineFn.class)) clazz = clazz.getSuperclass();
  return clazz != null && clazz.getGenericSuperclass() instanceof ParameterizedType;
}

Try / catch

try {
  sqlEnv.registerUdaf(name, myFn);
} catch (IllegalStateException e) {
  if (e.getMessage().contains("must be parameterized")) {
    throw new IllegalStateException("Add <InputT, AccumT, OutputT> to 'extends CombineFn<...>' in " + myFn.getClass().getName(), e);
  } else throw e;
}

Prevention

When it happens

Trigger: Registering a UDAF whose class hierarchy reaches CombineFn through a raw (non-generic) extends clause, e.g. class MyFn extends CombineFn, or an intermediate generic subclass instantiated without type arguments.

Common situations: Migrating legacy CombineFns written with raw types; defining CombineFn subclasses inside generic classes where the extends clause loses parameterization; copy-pasting a CombineFn but dropping the <...> type arguments.

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/7629c7da1cd44c3c. Report an issue: GitHub.