{"record":{"id":"7a7d3ce58e4762c8","repo":"apache/pulsar","slug":"input-topics-cannot-be-altered","errorCode":null,"errorMessage":"Input Topics cannot be altered","messagePattern":"Input Topics cannot be altered","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":1045,"sourceCode":"                        ConsumerConfig.builder()\n                                .serdeClassName(serdeClassName)\n                                .isRegexPattern(false)\n                                .build());\n            });\n        }\n        if (newConfig.getCustomSchemaInputs() != null) {\n            newConfig.getCustomSchemaInputs().forEach((topicName, schemaClassname) -> {\n                newConfig.getInputSpecs().put(topicName,\n                        ConsumerConfig.builder()\n                                .schemaType(schemaClassname)\n                                .isRegexPattern(false)\n                                .build());\n            });\n        }\n        if (!newConfig.getInputSpecs().isEmpty()) {\n            newConfig.getInputSpecs().forEach((topicName, consumerConfig) -> {\n                if (!existingConfig.getInputSpecs().containsKey(topicName)) {\n                    throw new IllegalArgumentException(\"Input Topics cannot be altered\");\n                }\n                if (consumerConfig.isRegexPattern() != existingConfig.getInputSpecs().get(topicName).isRegexPattern()) {\n                    throw new IllegalArgumentException(\n                            \"isRegexPattern for input topic \" + topicName + \" cannot be altered\");\n                }\n                mergedConfig.getInputSpecs().put(topicName, consumerConfig);\n            });\n        }\n        if (!StringUtils.isEmpty(newConfig.getOutputSerdeClassName()) && !newConfig.getOutputSerdeClassName()\n                .equals(existingConfig.getOutputSerdeClassName())) {\n            throw new IllegalArgumentException(\"Output Serde mismatch\");\n        }\n        if (!StringUtils.isEmpty(newConfig.getOutputSchemaType()) && !newConfig.getOutputSchemaType()\n                .equals(existingConfig.getOutputSchemaType())) {\n            throw new IllegalArgumentException(\"Output Schema mismatch\");\n        }\n        if (!StringUtils.isEmpty(newConfig.getLogTopic())) {\n            mergedConfig.setLogTopic(newConfig.getLogTopic());","sourceCodeStart":1027,"sourceCodeEnd":1063,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L1027-L1063","documentation":"Input topic subscriptions of a function are immutable across updates: you cannot add or remove input topics. This error is thrown when newConfig.getInputSpecs() contains a topic name that is not present in existingConfig.getInputSpecs().","triggerScenarios":"Updating a function while adding a new input topic to inputSpecs (or inputTopics) that the function was not originally created with.","commonSituations":"Widening a function's source topics after deployment; switching a function from one input topic to another in a single update; config drift between environments where input lists differ.","solutions":["Keep the input topic set identical to the existing function's set in the update.","To change inputs, delete and recreate the function with the desired input topics.","Note removing topics alone does not throw here, but adding a new one does — ensure the merged set matches existing keys."],"exampleFix":"// before\nnewConfig.addToInputSpecs(\"persistent://public/default/new-topic\", consumerConfig); // not in existing\n// after\n// recreate function instead:\n// 1) delete existing function  2) create with full new inputSpecs","handlingStrategy":"validation","validationCode":"Set<String> existingTopics = existingConfig.getInputSpecs().keySet();\nfor (String topic : newConfig.getInputSpecs().keySet()) {\n    if (!existingTopics.contains(topic)) {\n        throw new IllegalArgumentException(\"Cannot add input topic \" + topic + \" on update; recreate the function\");\n    }\n}","typeGuard":"boolean canApplyInputSpecs(FunctionConfig existing, FunctionConfig updated) {\n    return existing.getInputSpecs().keySet().containsAll(updated.getInputSpecs().keySet());\n}","tryCatchPattern":"try {\n    merged = FunctionConfigUtils.validateUpdate(existing, updated);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"Input Topics cannot be altered\")) {\n        throw new IllegalStateException(\"Recreate the function to change input topics\", e);\n    } else { throw e; }\n}","preventionTips":["Treat input topic sets as immutable per function instance.","Plan topic additions as delete+create deployments.","Diff new vs existing inputSpecs in CI before issuing updates."],"tags":["pulsar-functions","immutable-field","update-validation","input-topics"],"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"}