{"record":{"id":"629d25545bd444f2","repo":"apache/pulsar","slug":"sink-names-differ","errorCode":null,"errorMessage":"Sink Names differ","messagePattern":"Sink Names differ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java","lineNumber":619,"sourceCode":"    }\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        }\n\n        if (mergedConfig.getInputSpecs() == null) {\n            mergedConfig.setInputSpecs(new HashMap<>());\n        }\n        if (!StringUtils.isEmpty(newConfig.getLogTopic())) {\n            mergedConfig.setLogTopic(newConfig.getLogTopic());","sourceCodeStart":601,"sourceCodeEnd":637,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java#L601-L637","documentation":"Thrown by SinkConfigUtils.validateUpdate when an update attempt changes the sink's name. Immutable identity fields (tenant, namespace, name) are compared between the existing and new SinkConfig; any difference is rejected with IllegalArgumentException because renaming would create a different function resource.","triggerScenarios":"Calling Pulsar Functions update-sink (or admin API updateSink) with a SinkConfig whose getName() differs from the currently deployed sink, e.g. editing the 'name' field in a YAML config used for update.","commonSituations":"Developers copy a sink config from another sink or rename the sink in a config file, then run 'pulsar-admin sinks update' instead of 'create'; CI pipelines regenerate configs with a new name.","solutions":["Restore the original sink name in the config used for the update","If a rename is intended, delete the old sink and create a new one (update does not support rename)","Verify with 'pulsar-admin sinks get' which name is deployed and use that in tenant/namespace/name triple"],"exampleFix":"// before\nsinkConfig.setName(\"my-sink-v2\");\nadmin.sinks().updateSink(tenant, namespace, sinkConfig, null);\n// after\nsinkConfig.setName(\"my-sink\"); // keep existing name\nadmin.sinks().updateSink(tenant, namespace, sinkConfig, null);","handlingStrategy":"validation","validationCode":"if (!existing.getName().equals(newCfg.getName())) {\n    throw new IllegalArgumentException(\"Sink name is immutable; expected \" + existing.getName());\n}\nadmin.sinks().updateSink(existing.getTenant(), existing.getNamespace(), newCfg, null);","typeGuard":null,"tryCatchPattern":"try {\n    admin.sinks().updateSink(tenant, namespace, cfg, null);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Sink Names differ\")) { /* reset name to existing and retry once */ }\n    else throw e;\n}","preventionTips":["Always fetch the deployed sink config first and copy its tenant/namespace/name into the update payload","Never rename a sink in place; delete and recreate instead","Validate configs in CI by diffing against 'pulsar-admin sinks get' output"],"tags":["pulsar","functions","config-validation","immutable-field"],"backgroundTag":"immutable-config-field-update","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"}