apache/flink · error · IllegalArgumentException

Cannot set a UDF as combinable if it does not implement the

Error message

Cannot set a UDF as combinable if it does not implement the interface {}

What it means

Thrown by GroupReduceOperatorBase.setCombinable(boolean) when combinable is true but the user function class does not implement the GroupCombineFunction interface. A combinable group reduce pre-reduces data using the combine phase; this requires the UDF to implement GroupCombineFunction.combine(Iterable, Collector) so the combiner can call it.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/common/operators/base/GroupReduceOperatorBase.java:139

     * @return The secondary order.
     */
    public Ordering getGroupOrder() {
        return this.groupOrder;
    }

    /**
     * Marks the group reduce operation as combinable. Combinable operations may pre-reduce the data
     * before the actual group reduce operations. Combinable user-defined functions must implement
     * the interface {@link GroupCombineFunction}.
     *
     * @param combinable Flag to mark the group reduce operation as combinable.
     */
    public void setCombinable(boolean combinable) {
        // sanity check
        if (combinable
                && !GroupCombineFunction.class.isAssignableFrom(
                        this.userFunction.getUserCodeClass())) {
            throw new IllegalArgumentException(
                    "Cannot set a UDF as combinable if it does not implement the interface "
                            + GroupCombineFunction.class.getName());
        } else {
            this.combinable = combinable;
        }
    }

    /**
     * Checks whether the operation is combinable.
     *
     * @return True, if the UDF is combinable, false if not.
     * @see #setCombinable(boolean)
     */
    public boolean isCombinable() {
        return this.combinable;
    }

    public void setCustomPartitioner(Partitioner<?> customPartitioner) {

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Make the UDF implement both GroupReduceFunction and GroupCombineFunction (or extend CombineFunction which bridges them).
  2. If the UDF cannot be made combinable (e.g., it needs full group context), call setCombinable(false) or do not call it.
  3. Use @Combinable annotation only on classes that implement GroupCombineFunction.

Example fix

// before
public static class MyReducer extends GroupReduceFunction<MyType, MyType> {
    public void reduce(Iterable<MyType> values, Collector<MyType> out) { ... }
}
// elsewhere
reduceOp.setCombinable(true); // throws: MyReducer does not implement GroupCombineFunction

// after
public static class MyReducer extends RichGroupReduceFunction<MyType, MyType>
        implements GroupCombineFunction<MyType, MyType> {
    public void reduce(Iterable<MyType> values, Collector<MyType> out) { ... }
    public void combine(Iterable<MyType> values, Collector<MyType> out) { reduce(values, out); }
}
// elsewhere
reduceOp.setCombinable(true); // ok
Defensive patterns

Strategy: type-guard

Validate before calling

void safeSetCombinable(GroupReduceOperatorBase<?, ?, ?> op, boolean combinable) {
    if (combinable) {
    Class<?> udfClass = op.getUserFunctionWrapper().getUserCodeClass();
        if (!GroupCombineFunction.class.isAssignableFrom(udfClass)) {
            throw new IllegalStateException(
                "UDF " + udfClass.getName() + " must implement GroupCombineFunction to be combinable");
        }
    }
    op.setCombinable(combinable);
}

Type guard

static boolean isCombinableCapable(Class<?> udfClass) {
    return GroupCombineFunction.class.isAssignableFrom(udfClass);
}

Try / catch

try {
    reduceOp.setCombinable(true);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("GroupCombineFunction")) {
        // UDF does not implement GroupCombineFunction; either implement it or disable combine
        log.warn("Cannot enable combiner: {}", e.getMessage());
    }
}

Prevention

When it happens

Trigger: Calling reduceGroup with a function that only extends GroupReduceFunction, then calling setCombinable(true) on the operator. Annotating a non-GroupCombineFunction UDF with @Combinable. The UDF class implements GroupReduceFunction but not GroupCombineFunction.

Common situations: Writing a GroupReduceFunction and wanting combiner optimization without implementing the additional GroupCombineFunction interface. Forgetting that combinable requires implementing two interfaces. Copying a reduce function that was not designed for combining and enabling combine on it.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/0bd6b1c1e4c95e8c. Report an issue: GitHub.