{"record":{"id":"71899f39b174683c","repo":"apache/pulsar","slug":"retain-ordering-cannot-be-altered","errorCode":null,"errorMessage":"Retain Ordering cannot be altered","messagePattern":"Retain Ordering 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":1071,"sourceCode":"        }\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());\n        }\n        if (newConfig.getProcessingGuarantees() != null && !newConfig.getProcessingGuarantees()\n                .equals(existingConfig.getProcessingGuarantees())) {\n            throw new IllegalArgumentException(\"Processing Guarantees cannot be altered\");\n        }\n        if (newConfig.getRetainOrdering() != null && !newConfig.getRetainOrdering()\n                .equals(existingConfig.getRetainOrdering())) {\n            throw new IllegalArgumentException(\"Retain Ordering cannot be altered\");\n        }\n        if (newConfig.getRetainKeyOrdering() != null && !newConfig.getRetainKeyOrdering()\n                .equals(existingConfig.getRetainKeyOrdering())) {\n            throw new IllegalArgumentException(\"Retain Key Ordering cannot be altered\");\n        }\n        if (!StringUtils.isEmpty(newConfig.getOutput())) {\n            mergedConfig.setOutput(newConfig.getOutput());\n        }\n        if (newConfig.getUserConfig() != null) {\n            mergedConfig.setUserConfig(newConfig.getUserConfig());\n        }\n        if (newConfig.getSecrets() != null) {\n            mergedConfig.setSecrets(newConfig.getSecrets());\n        }\n        if (newConfig.getRuntime() != null && !newConfig.getRuntime().equals(existingConfig.getRuntime())) {\n            throw new IllegalArgumentException(\"Runtime cannot be altered\");\n        }\n        @SuppressWarnings(\"deprecation\")","sourceCodeStart":1053,"sourceCodeEnd":1089,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L1053-L1089","documentation":"FunctionConfigUtils.validateUpdate enforces that certain function settings are immutable after creation. retainOrdering changes would require a new subscription/consumption mode on the existing topic, so Pulsar rejects the update with IllegalArgumentException instead of applying it.","triggerScenarios":"Calling updateFunction (or the PUT admin API) with a FunctionConfig whose getRetainOrdering() is non-null and differs from the existing function's retainOrdering.","commonSituations":"Submitting an updated function config where the caller populated retainOrdering=true by default or changed it to fix ordering issues; client tooling that always serializes all fields, turning an unset field into an explicit conflicting value.","solutions":["Keep retainOrdering unchanged in the update request (pass null or the existing value fetched via getFunction).","If a different retainOrdering is truly needed, delete and re-create (or re-register) the function with the new value.","Diff the new config against the current config before submitting to catch the conflicting field.","Check client SDK/tooling defaults so retainOrdering is not implicitly set on updates."],"exampleFix":"// before\nFunctionConfig update = existing;\nupdate.setRetainOrdering(!existing.getRetainOrdering()); // rejected\nadmin.functions().updateFunction(update, configFile);\n// after\nFunctionConfig update = existing;\nupdate.setRetainOrdering(existing.getRetainOrdering()); // unchanged, or leave null\nadmin.functions().updateFunction(update, configFile);","handlingStrategy":"validation","validationCode":"FunctionConfig existing = admin.functions().getFunction(tenant, namespace, fnName);\nif (update.getRetainOrdering() != null && !update.getRetainOrdering().equals(existing.getRetainOrdering())) {\n    update.setRetainOrdering(null); // drop immutable change\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.functions().updateFunction(update, pkgFile);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"cannot be altered\")) { /* reset immutable fields and retry once */ }\n    else throw e;\n}","preventionTips":["Always fetch the current config with getFunction and modify only allowed fields.","Leave immutable fields null in update requests instead of copying defaults.","Diff the update against the existing config before submitting.","Re-create the function instead of updating when ordering semantics must change."],"tags":["pulsar-functions","config-update","immutable-field"],"backgroundTag":"immutable-config-field","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"}