{"record":{"id":"db8e4c6251ecb4a0","repo":"apache/pulsar","slug":"tenants-differ-db8e4c","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/SinkConfigUtils.java","lineNumber":613,"sourceCode":"            retval.addAll(sinkConfig.getTopicToSchemaType().keySet());\n        }\n        if (sinkConfig.getInputSpecs() != null) {\n            retval.addAll(sinkConfig.getInputSpecs().keySet());\n        }\n        return retval;\n    }\n\n    @SneakyThrows\n    public static SinkConfig clone(SinkConfig sinkConfig) {\n        return ObjectMapperFactory.getMapper().reader().readValue(\n                ObjectMapperFactory.getMapper().writer().writeValueAsBytes(sinkConfig), SinkConfig.class);\n    }\n\n    public static SinkConfig validateUpdate(SinkConfig existingConfig, SinkConfig newConfig) {\n        SinkConfig mergedConfig = clone(existingConfig);\n\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(\"Sink Names differ\");\n        }\n        if (!StringUtils.isEmpty(newConfig.getClassName())) {\n            mergedConfig.setClassName(newConfig.getClassName());\n        }\n        if (!StringUtils.isEmpty(newConfig.getSourceSubscriptionName()) && !newConfig.getSourceSubscriptionName()\n                .equals(existingConfig.getSourceSubscriptionName())) {\n            throw new IllegalArgumentException(\"Subscription Name cannot be altered\");\n        }\n\n        if (newConfig.getInputSpecs() == null) {\n            newConfig.setInputSpecs(new HashMap<>());\n        }","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java#L595-L631","documentation":"Update-compatibility check in SinkConfigUtils.validateUpdate: the existing sink and the new sink configuration belong to different tenants, and a sink update cannot move a sink across tenants (updates must keep tenant/namespace/name identity); the diverging tenant fields are the faulty input.","triggerScenarios":"Calling SinkConfigUtils.validateUpdate(existingConfig, newConfig) where newConfig.getTenant() differs from existingConfig.getTenant().","commonSituations":"Reusing an update request payload across tenants; programmatically cloning a sink config and changing tenant to 'move' the sink instead of delete+create.","solutions":["Keep the tenant unchanged; create a new sink under the other tenant"],"exampleFix":"// before\nnewConfig.setTenant(\"tenant-b\");\nadmin.sinks().updateSink(existingTenant, ns, name, newConfig);\n// after\nnewConfig.setTenant(existingTenant);\nadmin.sinks().updateSink(existingTenant, ns, name, newConfig);","handlingStrategy":"validation","validationCode":"if (!existing.getTenant().equals(newConfig.getTenant())) {\n  throw new IllegalStateException(\"Update cannot change tenant; delete+create instead\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  admin.sinks().updateSink(tenant, ns, name, newConfig);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage() != null && e.getMessage().equals(\"Tenants differ\")) {\n    // create the sink under the new tenant instead\n  } else { throw e; }\n}","preventionTips":["Build update configs from the existing config, overriding only mutable fields","Never trust tenant/namespace fields in client-supplied update bodies"],"tags":["pulsar-functions","sink","update","validation"],"backgroundTag":"identity-mismatch-on-update","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}