apache/beam · error · IllegalArgumentException
Unknown type of fn class
Error message
Unknown type of fn class %s
What it means
MapElements.expand only knows how to adapt two kinds of wrapped functions (aSerializableFunction vs ProcessFunction). If the internal fn object is neither, it cannot produce a DoFn and throws. This is a defensive branch that should be unreachable for fn objects created by MapElements' own factories.
Solutions
- Create MapElements only via the provided static via(...) factories (lambda, MethodReference, InferableFunction, or ProcessFunction)
- Do not subclass MapElements or set its fn field directly
- Check Beam version consistency between pipeline construction and deserialization
Example fix
// before
new MapElements<InputT, OutputT>(customFnWrapper) { ... }
// after
MapElements<InputT, OutputT> m = MapElements.via((InputT x) -> transform(x)); Defensive patterns
Strategy: validation
Validate before calling
if (!(fn instanceof SerializableFunction) && !(fn instanceof ProcessFunction)) {
throw new IllegalArgumentException("fn must be created via MapElements.via");
} Type guard
boolean isSupportedFn(Object fn) {
return fn instanceof ProcessFunction || fn instanceof SerializableFunction;
} Try / catch
try {
return mapElements.expand(input);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Unknown type of fn class")) {
throw new IllegalStateException("Rebuild MapElements via MapElements.via(...)" , e);
}
throw e;
} Prevention
- Only construct MapElements via the static via(...) factories
- Never subclass MapElements or assign fn directly
- Keep Beam versions consistent across all modules
When it happens
Trigger: Calling expand() on a MapElements whose fn was constructed as a type that is neither SerializableFunction-based nor ProcessFunction-based — typically only possible via reflection, custom subclasses, or corrupted serialization.
Common situations: Custom MapElements subclasses that bypass the standard factory methods (MapElements.via(...)); deserialization across incompatible Beam versions changing the fn wrapper class.
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
- Unable to locate declared method.
- Unexpected progress shape
- A function must be provided to convert the input type into…
- A PValue contained in
- A schema was provided without a data format (or viceversa)…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/0e6e33ed33335075.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/MapElements.java:155
.getClosure()
.apply(c.element(), Fn.Context.wrapProcessContext(c)));
}
})
.withSideInputs(
((Contextful<Fn<InputT, OutputT>>) fn).getRequirements().getSideInputs()));
} else if (fn instanceof ProcessFunction) {
return input.apply(
"Map",
ParDo.of(
new MapDoFn() {
@ProcessElement
public void processElement(
@Element InputT element, OutputReceiver<OutputT> receiver) throws Exception {
receiver.output(((ProcessFunction<InputT, OutputT>) fn).apply(element));
}
}));
} else {
throw new IllegalArgumentException(
String.format("Unknown type of fn class %s", fn.getClass()));
}
}
/** A DoFn implementation that handles a trivial map call. */
@SuppressWarnings("unused") // for outer
private abstract class MapDoFn extends DoFn<InputT, OutputT> {
/** Holds {@link MapDoFn#outer instance} of enclosing class, used by runner implementations. */
final MapElements<InputT, OutputT> outer = MapElements.this;
@Override
public void populateDisplayData(DisplayData.Builder builder) {
builder.delegate(MapElements.this);
}
@Override
public TypeDescriptor<InputT> getInputTypeDescriptor() {View on GitHub (pinned to 12126d8942)