apache/pulsar · error · IllegalArgumentException

When Guarantees == ATMOST_ONCE, autoAck must be equal to tru

Error message

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

What it means

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.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:390

        if (!StringUtils.isEmpty(functionConfig.getCustomRuntimeOptions())) {
            functionDetails.setCustomRuntimeOptions(functionConfig.getCustomRuntimeOptions());
        }

        if (isBuiltin) {
            String builtin = functionConfig.getJar().replaceFirst("^builtin://", "");
            functionDetails.setBuiltin(builtin);
        }

        return validateFunctionDetails(functionDetails);
    }

    @SuppressWarnings("deprecation")
    public static FunctionDetails validateFunctionDetails(FunctionDetails functionDetails)
            throws IllegalArgumentException {
        if (!functionDetails.isAutoAck() && functionDetails.getProcessingGuarantees()
                == ProcessingGuarantees.ATMOST_ONCE) {
            throw new IllegalArgumentException("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");
        }
        if (!functionDetails.isAutoAck()) {
            log.warn("The autoAck configuration will be deprecated in the future."
                    + " If you want not to automatically ack, please configure the processing guarantees as MANUAL.");
        }
        return functionDetails;
    }

    @SuppressWarnings("deprecation")
    public static FunctionConfig convertFromDetails(FunctionDetails functionDetails) {
        functionDetails = validateFunctionDetails(functionDetails);
        FunctionConfig functionConfig = new FunctionConfig();
        functionConfig.setTenant(functionDetails.getTenant());
        functionConfig.setNamespace(functionDetails.getNamespace());
        functionConfig.setName(functionDetails.getName());
        functionConfig.setParallelism(functionDetails.getParallelism());

View on GitHub (pinned to 820761864e)

Solutions

  1. Set processingGuarantees to MANUAL if you want to ack manually instead of disabling autoAck
  2. Remove setAutoAck(false) and let ATMOST_ONCE behave with autoAck=true
  3. Use ATMOST_ONCE with autoAck=true (default) since failed messages are not replayed anyway

Example fix

// before
config.setAutoAck(false);
config.setProcessingGuarantees(FunctionConfig.ProcessingGuarantees.ATMOST_ONCE);
// after
config.setProcessingGuarantees(FunctionConfig.ProcessingGuarantees.MANUAL);
Defensive patterns

Strategy: validation

Validate before calling

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"); }

Type guard

boolean isConsistentGuarantees(FunctionConfig c) { return !(Boolean.FALSE.equals(c.getAutoAck()) && c.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.ATMOST_ONCE); }

Try / catch

try { FunctionConfigUtils.validateFunctionDetails(details); } catch (IllegalArgumentException e) { log.error("Invalid guarantees/autoAck combo: {}", e.getMessage()); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/a922ddd6259fa6fe. Report an issue: GitHub.