apache/pulsar · error · IllegalArgumentException
Transform function class name must be set
Error message
Transform function class name must be set
What it means
After reading the FunctionDefinition from META-INF/services/pulsar-io.yaml, the library expects getFunctionClass() to name the transform function class. Thrown when the service descriptor exists but its functionClass field is null/empty.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java:497
String.format("Sink class %s not found", sinkClassName), e);
}
String functionClassName = sinkConfig.getTransformFunctionClassName();
TypeDefinition typeArg;
ValidatableFunctionPackage inputFunction;
if (transformFunction != null) {
// if function class name in sink config is not set, this should be a built-in function
// thus we should try to find it class name in the NAR service definition
if (functionClassName == null) {
FunctionDefinition functionDefinition =
transformFunction.getFunctionMetaData(FunctionDefinition.class);
if (functionDefinition == null) {
throw new IllegalArgumentException(
"Function package doesn't contain the META-INF/services/pulsar-io.yaml file.");
}
functionClassName = functionDefinition.getFunctionClass();
if (functionClassName == null) {
throw new IllegalArgumentException("Transform function class name must be set");
}
}
TypeDefinition functionClass;
try {
functionClass = transformFunction.resolveType(functionClassName);
} catch (TypePool.Resolution.NoSuchTypeException e) {
throw new IllegalArgumentException(
String.format("Function class %s not found", functionClassName), e);
}
// extract type from transform function class
if (!getRawFunctionTypes(functionClass, false)[1].asErasure().isAssignableTo(Record.class)) {
throw new IllegalArgumentException("Sink transform function output must be of type Record");
}
typeArg = getFunctionTypes(functionClass, false)[0];
inputFunction = transformFunction;
} else {
// extract type from sink class
typeArg = getSinkType(sinkClass);View on GitHub (pinned to 820761864e)
Solutions
- Add/repair the functionClass entry in META-INF/services/pulsar-io.yaml of the transform function package
- Or set sinkConfig.setTransformFunctionClassName(...) explicitly to bypass reading it from the descriptor
Example fix
# before name: my-transform # after name: my-transform functionClass: org.example.MyTransformFunction
Defensive patterns
Strategy: validation
Validate before calling
// verify descriptor declares functionClass
java.util.Properties p = new java.util.Properties();
// read META-INF/services/pulsar-io.yaml as text and assert 'functionClass:' key present
String yaml = new String(java.util.zip.GZIPInputStream.class.getResourceAsStream(
"/META-INF/services/pulsar-io.yaml").readAllBytes());
if (!yaml.contains("functionClass:")) throw new IllegalStateException("pulsar-io.yaml missing functionClass"); Try / catch
try {
admin.sinks().createSink(config, pkgPath);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("Transform function class name must be set")) {
config.setTransformFunctionClassName("org.example.MyTransform");
} else { throw e; }
} Prevention
- Keep the pulsar-io.yaml template with functionClass always filled
- Validate descriptor contents in CI before publishing the package
When it happens
Trigger: The pulsar-io.yaml in the transform function package exists but omits the functionClass property, and sinkConfig.transformFunctionClassName is null.
Common situations: Hand-written or templated pulsar-io.yaml missing the functionClass key; a build step that filtered the yaml resource out partially.
Related errors
- Sink class %s does not implement the correct interface
- Sink class %s not found
- Function package doesn't contain the META-INF/services/pulsa
- Function class %s not found
- Only one of serdeClassName or schemaType should be set
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/d0b5fe9ba4e40d67.
Report an issue: GitHub.