apache/pulsar · error · IllegalArgumentException
Function classname cannot be null
Error message
Function classname cannot be null
What it means
For Python runtime functions, doCommonChecks requires a className pointing to the Python class implementing the function (unlike Go, which needs none, and Java, whose className is checked separately in doJavaChecks). A null/empty className with Runtime.PYTHON throws this IllegalArgumentException.
Source
Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:802
"Output topic %s is also being used as an input topic (topics must be one or the other)",
outputTopic));
}
}
public static void doCommonChecks(FunctionConfig functionConfig) {
if (isEmpty(functionConfig.getTenant())) {
throw new IllegalArgumentException("Function tenant cannot be null");
}
if (isEmpty(functionConfig.getNamespace())) {
throw new IllegalArgumentException("Function namespace cannot be null");
}
if (isEmpty(functionConfig.getName())) {
throw new IllegalArgumentException("Function name cannot be null");
}
// go doesn't need className. Java className is done in doJavaChecks.
if (functionConfig.getRuntime() == FunctionConfig.Runtime.PYTHON) {
if (isEmpty(functionConfig.getClassName())) {
throw new IllegalArgumentException("Function classname cannot be null");
}
}
Collection<String> allInputTopics = collectAllInputTopics(functionConfig);
if (allInputTopics.isEmpty()) {
throw new IllegalArgumentException("No input topic(s) specified for the function");
}
for (String topic : allInputTopics) {
if (!TopicName.isValid(topic)) {
throw new IllegalArgumentException(String.format("Input topic %s is invalid", topic));
}
}
if (!isEmpty(functionConfig.getOutput())) {
if (!TopicName.isValid(functionConfig.getOutput())) {
throw new IllegalArgumentException(
String.format("Output topic %s is invalid", functionConfig.getOutput()));
}View on GitHub (pinned to 820761864e)
Solutions
- Set the fully-qualified Python class: functionConfig.setClassName("my.module.MyFunction").
- Add the 'className' key to the Python function's YAML/JSON config.
- Ensure the class actually exists in the submitted Python payload/archive and matches the module path.
- If the function is Go, confirm the runtime is set to GOLANG — then className is not required.
Example fix
// before
config.setRuntime(FunctionConfig.Runtime.PYTHON);
config.setTenant("public");
config.setNamespace("default");
// after
config.setRuntime(FunctionConfig.Runtime.PYTHON);
config.setClassName("examples.function.MyFunction");
config.setTenant("public");
config.setNamespace("default"); Defensive patterns
Strategy: validation
Validate before calling
if (config.getRuntime() == FunctionConfig.Runtime.PYTHON
&& (config.getClassName() == null || config.getClassName().trim().isEmpty())) {
throw new IllegalArgumentException("Python functions require FunctionConfig.className");
} Type guard
static boolean hasRequiredClassName(FunctionConfig c) {
if (c == null || c.getRuntime() == null) return false;
switch (c.getRuntime()) {
case PYTHON: return c.getClassName() != null && !c.getClassName().trim().isEmpty();
case GOLANG: return true; // className not required for Go
default: return true; // Java checked separately in doJavaChecks
}
} Try / catch
try {
FunctionConfigUtils.validateNonJavaFunction(functionConfig, null);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("classname")) {
throw new IllegalStateException("Python functions need 'className' (e.g. mymodule.MyFunction)", e);
}
throw e;
} Prevention
- Set className whenever you set runtime to PYTHON — do it in the same builder step.
- Verify the class exists inside the submitted Python archive (module path matches className).
- Don't reuse Go function config templates for Python functions without adding className.
- Keep separate validated templates per runtime (Java/Python/Go).
When it happens
Trigger: Submitting a function with functionConfig.setRuntime(FunctionConfig.Runtime.PYTHON) but without setClassName("mymodule.MyFunction"), through validateNonJavaFunction, the REST API, or pulsar-admin; Go functions with no className do NOT trigger this — only PYTHON configs.
Common situations: Copying a Go function config template for a Python function (Go configs omit className legitimately); YAML config with 'runtime: PYTHON' but no 'className'; typos in the class name field key.
Understand the failure class
Background: "X is required", "field cannot be empty", error-the-field-is-required: missing required-field validation errors, explained — this error's family across 39 libraries.
Related errors
- When Guarantees == ATMOST_ONCE, autoAck must be equal to tru
- Function class name is not provided.
- Only one of schemaType or serdeClassName should be set in in
- Only one of outputSchemaType or outputSerdeClassName should
- Effectively-once processing guarantees not yet supported in
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/8057170f2bc83d95.
Report an issue: GitHub.