{"record":{"id":"911ad40a02eb49ba","repo":"apache/pulsar","slug":"tenants-differ","errorCode":null,"errorMessage":"Tenants differ","messagePattern":"Tenants differ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":988,"sourceCode":"        if (functionConfig.getRuntime() == FunctionConfig.Runtime.GO) {\n            doGolangChecks(functionConfig);\n        } else if (functionConfig.getRuntime() == FunctionConfig.Runtime.PYTHON) {\n            doPythonChecks(functionConfig);\n        } else {\n            throw new IllegalArgumentException(\"Function language runtime is either not set or cannot be determined\");\n        }\n    }\n\n    public static ExtractedFunctionDetails validateJavaFunction(FunctionConfig functionConfig,\n                                                                ValidatableFunctionPackage validatableFunctionPackage) {\n        doCommonChecks(functionConfig);\n        return doJavaChecks(functionConfig, validatableFunctionPackage);\n    }\n\n    public static FunctionConfig validateUpdate(FunctionConfig existingConfig, FunctionConfig newConfig) {\n        FunctionConfig mergedConfig = existingConfig.toBuilder().build();\n        if (!existingConfig.getTenant().equals(newConfig.getTenant())) {\n            throw new IllegalArgumentException(\"Tenants differ\");\n        }\n        if (!existingConfig.getNamespace().equals(newConfig.getNamespace())) {\n            throw new IllegalArgumentException(\"Namespaces differ\");\n        }\n        if (!existingConfig.getName().equals(newConfig.getName())) {\n            throw new IllegalArgumentException(\"Function Names differ\");\n        }\n        if (!StringUtils.isEmpty(newConfig.getClassName())) {\n            mergedConfig.setClassName(newConfig.getClassName());\n        }\n\n        if (!StringUtils.isEmpty(newConfig.getJar())) {\n            mergedConfig.setJar(newConfig.getJar());\n        }\n\n        if (newConfig.getInputSpecs() == null) {\n            newConfig.setInputSpecs(new HashMap<>());\n        }","sourceCodeStart":970,"sourceCodeEnd":1006,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L970-L1006","documentation":"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.","triggerScenarios":"Calling the update function admin API (PUT on an existing function) with a FunctionConfig whose getTenant() differs from the currently registered function's tenant.","commonSituations":"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.","solutions":["Keep the tenant identical to the existing function in the update payload.","If a different tenant is genuinely needed, delete the function and create a new one under the target tenant.","Verify the tenant name spelling/case matches the existing registration exactly."],"exampleFix":"// before\nupdateConfig.setTenant(\"public-other\"); // differs from existing\n// after\nupdateConfig.setTenant(existingConfig.getTenant()); // e.g. \"public\"","handlingStrategy":"validation","validationCode":"if (!existingConfig.getTenant().equals(newConfig.getTenant())) {\n    throw new IllegalArgumentException(\"Update payload must reuse tenant \" + existingConfig.getTenant());\n}","typeGuard":"boolean sameIdentity(FunctionConfig a, FunctionConfig b) {\n    return a.getTenant().equals(b.getTenant())\n        && a.getNamespace().equals(b.getNamespace())\n        && a.getName().equals(b.getName());\n}","tryCatchPattern":"try {\n    merged = FunctionConfigUtils.validateUpdate(existing, updated);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"Tenants differ\")) {\n        updated.setTenant(existing.getTenant());\n        merged = FunctionConfigUtils.validateUpdate(existing, updated);\n    } else { throw e; }\n}","preventionTips":["Load the existing config via the admin API and copy its tenant/namespace/name into the update payload.","Never edit identity fields in deploy templates.","Recreate (delete + create) when a tenant move is actually intended."],"tags":["pulsar-functions","immutable-field","update-validation"],"backgroundTag":"immutable-field-update-rejected","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}