apache/pulsar · error · IllegalArgumentException

Function tenant cannot be null

Error message

Function tenant cannot be null

What it means

FunctionConfigUtils.doCommonChecks validates a function's FunctionConfig before it can be created or updated. The first mandatory field is the tenant, the top-level namespace grouping in Pulsar. If tenant is null or empty, validation fails immediately with this IllegalArgumentException so an incomplete function config never reaches the broker.

Solutions

  1. Set the tenant on the FunctionConfig before submitting, e.g. functionConfig.setTenant("public") (or pass --tenant to the CLI).
  2. If loading from YAML/JSON, add the missing 'tenant' key to the config file.
  3. Verify the field is populated after deserialization; empty-string tenants also fail, so trim/require a non-empty value.
  4. Use a tenant that exists in the cluster (commonly 'public') if you are unsure which one to use.

Example fix

// before
FunctionConfig config = new FunctionConfig();
config.setNamespace("default");
config.setName("my-fn");
// after
FunctionConfig config = new FunctionConfig();
config.setTenant("public");
config.setNamespace("default");
config.setName("my-fn");
Defensive patterns

Strategy: validation

Validate before calling

if (config == null || config.getTenant() == null || config.getTenant().trim().isEmpty()) {
    throw new IllegalArgumentException("FunctionConfig.tenant must be set before submission");
}

Type guard

static boolean hasTenant(FunctionConfig c) {
    return c != null && c.getTenant() != null && !c.getTenant().trim().isEmpty();
}

Try / catch

try {
    FunctionConfigUtils.validateNonJavaFunction(functionConfig, null);
} catch (IllegalArgumentException e) {
    log.error("Invalid function config: {}", e.getMessage());
    // surface a friendly message asking for --tenant
}

Prevention

When it happens

Trigger: Calling createFunction/updateFunction (via FunctionConfigUtils' createFunction paths, the functions worker REST API, or pulsar-admin functions create) with a FunctionConfig whose getTenant() returns null or the empty string; doCommonChecks is invoked from validateNonJavaFunction and validateJavaFunction.

Common situations: Building a FunctionConfig programmatically and forgetting setTenant(); YAML/JSON function config files that omit the 'tenant' key; CLI invocations missing the --tenant flag; configs deserialized from templates where tenant was left as a placeholder.

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/a83051718aabb06b. Report an issue: GitHub.

Appendix: source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:791

        if (functionConfig.getMaxMessageRetries() != null && functionConfig.getMaxMessageRetries() >= 0) {
            throw new IllegalArgumentException("Message retries not yet supported in Go function");
        }
    }

    private static void verifyNoTopicClash(Collection<String> inputTopics, String outputTopic)
            throws IllegalArgumentException {
        if (inputTopics.contains(outputTopic)) {
            throw new IllegalArgumentException(
                    String.format(
                            "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");
        }

View on GitHub (pinned to 820761864e)