{"record":{"id":"97c132faaa4d1962","repo":"apache/pulsar","slug":"function-names-differ-97c132","errorCode":null,"errorMessage":"Function Names differ","messagePattern":"Function Names differ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SourceConfigUtils.java","lineNumber":389,"sourceCode":"        return new ExtractedSourceDetails(sourceClassName, typeArg.asErasure().getTypeName());\n    }\n\n    @SneakyThrows\n    public static SourceConfig clone(SourceConfig sourceConfig) {\n        return ObjectMapperFactory.getMapper().reader().readValue(\n                ObjectMapperFactory.getMapper().writer().writeValueAsBytes(sourceConfig), SourceConfig.class);\n    }\n\n    public static SourceConfig validateUpdate(SourceConfig existingConfig, SourceConfig newConfig) {\n        SourceConfig mergedConfig = clone(existingConfig);\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        if (!StringUtils.isEmpty(newConfig.getTopicName())) {\n            mergedConfig.setTopicName(newConfig.getTopicName());\n        }\n        if (!StringUtils.isEmpty(newConfig.getSerdeClassName())) {\n            mergedConfig.setSerdeClassName(newConfig.getSerdeClassName());\n        }\n        if (!StringUtils.isEmpty(newConfig.getSchemaType())) {\n            mergedConfig.setSchemaType(newConfig.getSchemaType());\n        }\n        if (newConfig.getConfigs() != null) {\n            mergedConfig.setConfigs(newConfig.getConfigs());\n        }\n        if (newConfig.getSecrets() != null) {\n            mergedConfig.setSecrets(newConfig.getSecrets());","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SourceConfigUtils.java#L371-L407","documentation":"Update-compatibility check in SourceConfigUtils.validateUpdate: the source's function name differs between the existing and new configuration (the tenant/namespace identity checks precede it), and renaming is not permitted via update; the changed function name is the faulty input.","triggerScenarios":"validateUpdate receives a newConfig whose getName() differs from the existing config's name — e.g. an update payload for one source accidentally applied to another, or the name edited in a config template.","commonSituations":"Copy-pasting a source config, editing the name, then submitting it as an update to the original source; CI tooling that derives the name from a branch/feature flag; off-by-one in scripts iterating source configs.","solutions":["Keep the name identical when updating; delete+create to rename"],"exampleFix":"// before\n{\"tenant\":\"t1\",\"namespace\":\"ns1\",\"name\":\"src-renamed\", ...}\n// after\n{\"tenant\":\"t1\",\"namespace\":\"ns1\",\"name\":\"src1\", ...}","handlingStrategy":"validation","validationCode":"if (!Objects.equals(existing.getName(), update.getName())) {\n    throw new IllegalArgumentException(\"name is immutable on update\");\n}","typeGuard":null,"tryCatchPattern":"try { SourceConfigUtils.validateUpdate(existing, update); } catch (IllegalArgumentException e) { if (e.getMessage().equals(\"Function Names differ\")) { update.setName(existing.getName()); } else { throw e; } }","preventionTips":["Treat tenant/namespace/name as a primary key — never mutate in updates","For renames, use delete + create","Guard scripts that generate configs so the name field is read-only on update paths"],"tags":["pulsar-functions","immutable-field","update-validation"],"backgroundTag":"immutable-field-change","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"}