apache/pulsar · error · IllegalArgumentException

AutoAck cannot be altered

Error message

AutoAck cannot be altered

What it means

Thrown by SinkConfigUtils.validateUpdate when the deprecated autoAck field is set in the new config and differs from the existing value. autoAck is deprecated and immutable to prevent changing acknowledgment semantics on a running sink.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/SinkConfigUtils.java:708

        if (newConfig.getSecrets() != null) {
            mergedConfig.setSecrets(newConfig.getSecrets());
        }
        if (newConfig.getParallelism() != null) {
            mergedConfig.setParallelism(newConfig.getParallelism());
        }
        if (newConfig.getRetainOrdering() != null && !newConfig.getRetainOrdering()
                .equals(existingConfig.getRetainOrdering())) {
            throw new IllegalArgumentException("Retain Ordering cannot be altered");
        }
        if (newConfig.getRetainKeyOrdering() != null && !newConfig.getRetainKeyOrdering()
                .equals(existingConfig.getRetainKeyOrdering())) {
            throw new IllegalArgumentException("Retain Key Ordering cannot be altered");
        }
        @SuppressWarnings("deprecation")
        boolean autoAckChanged = newConfig.getAutoAck() != null
                && !newConfig.getAutoAck().equals(existingConfig.getAutoAck());
        if (autoAckChanged) {
            throw new IllegalArgumentException("AutoAck cannot be altered");
        }
        if (newConfig.getResources() != null) {
            mergedConfig
                    .setResources(ResourceConfigUtils.merge(existingConfig.getResources(), newConfig.getResources()));
        }
        if (newConfig.getTimeoutMs() != null) {
            mergedConfig.setTimeoutMs(newConfig.getTimeoutMs());
        }
        if (newConfig.getCleanupSubscription() != null) {
            mergedConfig.setCleanupSubscription(newConfig.getCleanupSubscription());
        }
        if (!StringUtils.isEmpty(newConfig.getArchive())) {
            mergedConfig.setArchive(newConfig.getArchive());
        }
        if (!StringUtils.isEmpty(newConfig.getRuntimeFlags())) {
            mergedConfig.setRuntimeFlags(newConfig.getRuntimeFlags());
        }
        if (!StringUtils.isEmpty(newConfig.getCustomRuntimeOptions())) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Remove the autoAck field from the update config entirely (it is deprecated)
  2. If present, set it to the same value as the existing sink
  3. Migrate config files to stop emitting autoAck

Example fix

// before
sinkConfig.setAutoAck(false); // deprecated, deployed sink has true
admin.sinks().updateSink(tenant, namespace, sinkConfig, null);
// after
// (do not set autoAck at all)
admin.sinks().updateSink(tenant, namespace, sinkConfig, null);
Defensive patterns

Strategy: validation

Validate before calling

if (newCfg.getAutoAck() != null
    && !newCfg.getAutoAck().equals(existing.getAutoAck())) {
    throw new IllegalArgumentException("autoAck is deprecated and immutable; remove it from the config");
}

Try / catch

try {
    admin.sinks().updateSink(tenant, namespace, cfg, null);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("AutoAck cannot be altered")) { /* strip autoAck field and retry */ }
    else throw e;
}

Prevention

When it happens

Trigger: Updating a sink whose config carries an autoAck value different from the deployed one; older configs serialized with autoAck: true/false are used against a sink created with the opposite value.

Common situations: Legacy configs from old Pulsar versions still containing autoAck; tool-generated configs that emit the deprecated field with a default differing from the deployed sink.

Related errors


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