{"record":{"id":"a922ddd6259fa6fe","repo":"apache/pulsar","slug":"when-guarantees-atmost-once-autoack-must-be-eq","errorCode":null,"errorMessage":"When Guarantees == ATMOST_ONCE, autoAck must be equal to true. This is a contradictory configuration, autoAck will be removed later. Please refer to PIP: https://github.com/apache/pulsar/issues/15560","messagePattern":"When Guarantees == ATMOST_ONCE, autoAck must be equal to true\\. This is a contradictory configuration, autoAck will be removed later\\. Please refer to PIP: https://github\\.com/apache/pulsar/issues/15560","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":390,"sourceCode":"\n        if (!StringUtils.isEmpty(functionConfig.getCustomRuntimeOptions())) {\n            functionDetails.setCustomRuntimeOptions(functionConfig.getCustomRuntimeOptions());\n        }\n\n        if (isBuiltin) {\n            String builtin = functionConfig.getJar().replaceFirst(\"^builtin://\", \"\");\n            functionDetails.setBuiltin(builtin);\n        }\n\n        return validateFunctionDetails(functionDetails);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static FunctionDetails validateFunctionDetails(FunctionDetails functionDetails)\n            throws IllegalArgumentException {\n        if (!functionDetails.isAutoAck() && functionDetails.getProcessingGuarantees()\n                == ProcessingGuarantees.ATMOST_ONCE) {\n            throw new IllegalArgumentException(\"When Guarantees == ATMOST_ONCE, autoAck must be equal to true.\"\n                    + \" This is a contradictory configuration, autoAck will be removed later.\"\n                    + \" Please refer to PIP: https://github.com/apache/pulsar/issues/15560\");\n        }\n        if (!functionDetails.isAutoAck()) {\n            log.warn(\"The autoAck configuration will be deprecated in the future.\"\n                    + \" If you want not to automatically ack, please configure the processing guarantees as MANUAL.\");\n        }\n        return functionDetails;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static FunctionConfig convertFromDetails(FunctionDetails functionDetails) {\n        functionDetails = validateFunctionDetails(functionDetails);\n        FunctionConfig functionConfig = new FunctionConfig();\n        functionConfig.setTenant(functionDetails.getTenant());\n        functionConfig.setNamespace(functionDetails.getNamespace());\n        functionConfig.setName(functionDetails.getName());\n        functionConfig.setParallelism(functionDetails.getParallelism());","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L372-L408","documentation":"Apache Pulsar function validation rejects a FunctionDetails where autoAck is false while processingGuarantees is ATMOST_ONCE. Manual acking (autoAck=false) is meaningless under at-most-once semantics because messages are not re-delivered, so this combination is contradictory and autoAck is being deprecated in favor of processing guarantees (PIP #15560). validateFunctionDetails enforces this before a function is submitted.","triggerScenarios":"Calling FunctionConfigUtils.convert / convertFromDetails on a config where setAutoAck(false) is combined with setProcessingGuarantees(ATMOST_ONCE), or submitting such a function via the admin API / CLI.","commonSituations":"Users porting older configs that explicitly disabled autoAck; copy-pasted examples setting autoAck=false while keeping the default ATMOST_ONCE guarantee; migration confusion after the autoAck deprecation.","solutions":["Set processingGuarantees to MANUAL if you want to ack manually instead of disabling autoAck","Remove setAutoAck(false) and let ATMOST_ONCE behave with autoAck=true","Use ATMOST_ONCE with autoAck=true (default) since failed messages are not replayed anyway"],"exampleFix":"// before\nconfig.setAutoAck(false);\nconfig.setProcessingGuarantees(FunctionConfig.ProcessingGuarantees.ATMOST_ONCE);\n// after\nconfig.setProcessingGuarantees(FunctionConfig.ProcessingGuarantees.MANUAL);","handlingStrategy":"validation","validationCode":"if (cfg.getAutoAck() != null && !cfg.getAutoAck() && cfg.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.ATMOST_ONCE) { throw new IllegalArgumentException(\"Set processingGuarantees=MANUAL instead of autoAck=false with ATMOST_ONCE\"); }","typeGuard":"boolean isConsistentGuarantees(FunctionConfig c) { return !(Boolean.FALSE.equals(c.getAutoAck()) && c.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.ATMOST_ONCE); }","tryCatchPattern":"try { FunctionConfigUtils.validateFunctionDetails(details); } catch (IllegalArgumentException e) { log.error(\"Invalid guarantees/autoAck combo: {}\", e.getMessage()); }","preventionTips":["Never set autoAck=false; use MANUAL processing guarantee for manual acking","Run FunctionConfigUtils.convert on configs in CI before deploying","Keep up with autoAck deprecation notices (PIP 15560)"],"tags":["pulsar","functions","configuration","validation"],"backgroundTag":"contradictory-processing-guarantee-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"}