{"record":{"id":"80ee80da8a1c4812","repo":"apache/pulsar","slug":"only-one-of-retain-ordering-or-retain-key-ordering","errorCode":null,"errorMessage":"Only one of retain ordering or retain key ordering can be set","messagePattern":"Only one of retain ordering or retain key ordering can be set","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":887,"sourceCode":"\n        if (functionConfig.getMaxMessageRetries() != null && functionConfig.getMaxMessageRetries() >= 0\n                && functionConfig.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.EFFECTIVELY_ONCE) {\n            throw new IllegalArgumentException(\"MaxMessageRetries and Effectively once don't gel well\");\n        }\n        if ((functionConfig.getMaxMessageRetries() == null || functionConfig.getMaxMessageRetries() < 0)\n                && !org.apache.commons.lang3.StringUtils.isEmpty(functionConfig.getDeadLetterTopic())) {\n            throw new IllegalArgumentException(\"Dead Letter Topic specified, however max retries is set to infinity\");\n        }\n        if (functionConfig.getRetainKeyOrdering() != null\n                && functionConfig.getRetainKeyOrdering()\n                && functionConfig.getProcessingGuarantees() != null\n                && functionConfig.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.EFFECTIVELY_ONCE) {\n            throw new IllegalArgumentException(\n                    \"When effectively once processing guarantee is specified, retain Key ordering cannot be set\");\n        }\n        if (functionConfig.getRetainKeyOrdering() != null && functionConfig.getRetainKeyOrdering()\n                && functionConfig.getRetainOrdering() != null && functionConfig.getRetainOrdering()) {\n            throw new IllegalArgumentException(\"Only one of retain ordering or retain key ordering can be set\");\n        }\n\n        if (!isEmpty(functionConfig.getPy()) && !org.apache.pulsar.common.functions.Utils\n                .isFunctionPackageUrlSupported(functionConfig.getPy())\n                && functionConfig.getPy().startsWith(BUILTIN)) {\n            String filename = functionConfig.getPy();\n            if (filename.contains(\"..\")) {\n                throw new IllegalArgumentException(\"Invalid filename: \" + filename);\n            }\n\n            if (!new File(filename).exists()) {\n                throw new IllegalArgumentException(\"The supplied python file does not exist\");\n            }\n        }\n        if (!isEmpty(functionConfig.getGo()) && !org.apache.pulsar.common.functions.Utils\n                .isFunctionPackageUrlSupported(functionConfig.getGo())\n                && functionConfig.getGo().startsWith(BUILTIN)) {\n            String filename = functionConfig.getGo();","sourceCodeStart":869,"sourceCodeEnd":905,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L869-L905","documentation":"The FunctionConfig rejects setting both retainOrdering=true and retainKeyOrdering=true at the same time. Retain ordering is global message ordering across the function's subscriptions, while retain key ordering is per-key ordering; the framework treats requesting both as ambiguous and throws IllegalArgumentException during config validation in doCommonChecks.","triggerScenarios":"Creating or updating a function with a FunctionConfig where setRetainOrdering(true) and setRetainKeyOrdering(true) are both present, via validateJavaFunction/validateNonJavaFunction or the REST admin API.","commonSituations":"Merging config from two templates that each set one of the flags; toggling between global and key-based ordering by adding the second flag instead of switching; misunderstanding the flags as independent toggles that can be combined.","solutions":["Pick one ordering mode: remove setRetainKeyOrdering(true) if global ordering is needed","Remove setRetainOrdering(true) if per-key ordering is what you need","If ordering is not actually required, set both flags to false/null"],"exampleFix":"// before\nconf.setRetainOrdering(true);\nconf.setRetainKeyOrdering(true);\n// after\nconf.setRetainOrdering(false);\nconf.setRetainKeyOrdering(true); // only one ordering mode allowed","handlingStrategy":"validation","validationCode":"if (Boolean.TRUE.equals(conf.getRetainOrdering()) && Boolean.TRUE.equals(conf.getRetainKeyOrdering())) {\n    throw new IllegalStateException(\"Set only one of retainOrdering or retainKeyOrdering\");\n}","typeGuard":"boolean hasSingleOrderingMode(FunctionConfig c) {\n    int flags = (Boolean.TRUE.equals(c.getRetainOrdering()) ? 1 : 0)\n              + (Boolean.TRUE.equals(c.getRetainKeyOrdering()) ? 1 : 0);\n    return flags <= 1;\n}","tryCatchPattern":"try {\n    admin.functions().createFunction(conf);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"retain ordering\")) {\n        conf.setRetainKeyOrdering(false); // prefer global retainOrdering\n        admin.functions().createFunction(conf);\n    } else throw e;\n}","preventionTips":["Treat the two ordering flags as an enum-like choice in your config layer","When toggling one flag on, explicitly clear the other","Document in team config templates which ordering mode each pipeline uses"],"tags":["pulsar-functions","config-validation","illegal-argument"],"backgroundTag":"incompatible-function-config","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"}