apache/pulsar · error · IllegalArgumentException

Tenants differ

Error message

Tenants differ

What it means

validateUpdate merges a new function config into an existing one, but the function's identity (tenant, namespace, name) is immutable. This error is thrown when the new config's tenant does not match the existing config's tenant, i.e. an update attempts to move the function to a different tenant.

Source

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

        if (functionConfig.getRuntime() == FunctionConfig.Runtime.GO) {
            doGolangChecks(functionConfig);
        } else if (functionConfig.getRuntime() == FunctionConfig.Runtime.PYTHON) {
            doPythonChecks(functionConfig);
        } else {
            throw new IllegalArgumentException("Function language runtime is either not set or cannot be determined");
        }
    }

    public static ExtractedFunctionDetails validateJavaFunction(FunctionConfig functionConfig,
                                                                ValidatableFunctionPackage validatableFunctionPackage) {
        doCommonChecks(functionConfig);
        return doJavaChecks(functionConfig, validatableFunctionPackage);
    }

    public static FunctionConfig validateUpdate(FunctionConfig existingConfig, FunctionConfig newConfig) {
        FunctionConfig mergedConfig = existingConfig.toBuilder().build();
        if (!existingConfig.getTenant().equals(newConfig.getTenant())) {
            throw new IllegalArgumentException("Tenants differ");
        }
        if (!existingConfig.getNamespace().equals(newConfig.getNamespace())) {
            throw new IllegalArgumentException("Namespaces differ");
        }
        if (!existingConfig.getName().equals(newConfig.getName())) {
            throw new IllegalArgumentException("Function Names differ");
        }
        if (!StringUtils.isEmpty(newConfig.getClassName())) {
            mergedConfig.setClassName(newConfig.getClassName());
        }

        if (!StringUtils.isEmpty(newConfig.getJar())) {
            mergedConfig.setJar(newConfig.getJar());
        }

        if (newConfig.getInputSpecs() == null) {
            newConfig.setInputSpecs(new HashMap<>());
        }

View on GitHub (pinned to 820761864e)

Solutions

  1. Keep the tenant identical to the existing function in the update payload.
  2. If a different tenant is genuinely needed, delete the function and create a new one under the target tenant.
  3. Verify the tenant name spelling/case matches the existing registration exactly.

Example fix

// before
updateConfig.setTenant("public-other"); // differs from existing
// after
updateConfig.setTenant(existingConfig.getTenant()); // e.g. "public"
Defensive patterns

Strategy: validation

Validate before calling

if (!existingConfig.getTenant().equals(newConfig.getTenant())) {
    throw new IllegalArgumentException("Update payload must reuse tenant " + existingConfig.getTenant());
}

Type guard

boolean sameIdentity(FunctionConfig a, FunctionConfig b) {
    return a.getTenant().equals(b.getTenant())
        && a.getNamespace().equals(b.getNamespace())
        && a.getName().equals(b.getName());
}

Try / catch

try {
    merged = FunctionConfigUtils.validateUpdate(existing, updated);
} catch (IllegalArgumentException e) {
    if (e.getMessage().equals("Tenants differ")) {
        updated.setTenant(existing.getTenant());
        merged = FunctionConfigUtils.validateUpdate(existing, updated);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling the update function admin API (PUT on an existing function) with a FunctionConfig whose getTenant() differs from the currently registered function's tenant.

Common situations: Reusing a config file meant for a different tenant/environment; templated deployments where a tenant placeholder changed between create and update; copy-paste between tenants.

Related errors


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