apache/pulsar · error · IllegalArgumentException
Function class %s not found
Error message
Function class %s not found
What it means
Thrown when the transform function class name resolved from config/service descriptor cannot be found in the transform function package's type pool. Mirrors error 1470 but for the transform function class of a sink with a transform stage.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java:504
// 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);
inputFunction = sinkFunction;
}
if (sinkConfig.getTopicToSerdeClassName() != null) {
for (String serdeClassName : sinkConfig.getTopicToSerdeClassName().values()) {
ValidatorUtils.validateSerde(serdeClassName, typeArg, inputFunction.getTypePool(), true);
}View on GitHub (pinned to 820761864e)
Solutions
- Check that transformFunctionClassName matches a class in the uploaded archive (`jar tf`)
- Re-upload a package containing the transform function class
- Fix the functionClass entry in META-INF/services/pulsar-io.yaml if that is the source of the name
Example fix
// before
sinkConfig.setTransformFunctionClassName("org.example.OldTransform");
// after
sinkConfig.setTransformFunctionClassName("org.example.NewTransform"); Defensive patterns
Strategy: validation
Validate before calling
try (java.util.jar.JarFile jar = new java.util.jar.JarFile(pkgPath)) {
String entry = transformClassName.replace('.', '/') + ".class";
if (jar.getJarEntry(entry) == null) {
throw new IllegalStateException("Transform class " + transformClassName + " not in package");
}
} Try / catch
try {
admin.sinks().createSink(config, pkgPath);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Function class")) {
log.error("transformFunctionClassName not found in package; align config with artifact", e);
} else { throw e; }
} Prevention
- Regenerate sink configs after refactoring/renaming transform classes
- Keep config class names in the same repo/build as the package
- `jar tf` check in deployment scripts
When it happens
Trigger: sinkConfig.transformFunctionClassName (or the value from pulsar-io.yaml) names a class absent from the uploaded transform function archive.
Common situations: Typos in the function class name; class renamed/refactored between versions; package uploaded is stale relative to the config.
Related errors
- Sink class %s not found
- Sink class %s does not implement the correct interface
- Transform function class name must be set
- Only one of serdeClassName or schemaType should be set
- When effectively once processing guarantee is specified, ret
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/941de66fa8008a45.
Report an issue: GitHub.