apache/pulsar · error · IllegalArgumentException

Retain Ordering cannot be altered

Error message

Retain Ordering cannot be altered

What it means

Thrown by SinkConfigUtils.validateUpdate when retainOrdering is set and differs from the existing sink's value. Ordering retention is an immutable deployment property; flipping it via update would change how messages are dispatched to sink instances.

Source

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

                finalMergedConfig.getInputSpecs().put(topicName, consumerConfig);
            });
        }
        if (newConfig.getProcessingGuarantees() != null && !newConfig.getProcessingGuarantees()
                .equals(existingConfig.getProcessingGuarantees())) {
            throw new IllegalArgumentException("Processing Guarantees cannot be altered");
        }
        if (newConfig.getConfigs() != null) {
            mergedConfig.setConfigs(newConfig.getConfigs());
        }
        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());
        }

View on GitHub (pinned to 820761864e)

Solutions

  1. Set retainOrdering to the same value as the existing sink, or leave it null
  2. If ordering must change, delete and recreate the sink
  3. Inspect the deployed config's retainOrdering before updating

Example fix

// before
sinkConfig.setRetainOrdering(true); // deployed sink has false
admin.sinks().updateSink(tenant, namespace, sinkConfig, null);
// after
sinkConfig.setRetainOrdering(false); // matches existing
admin.sinks().updateSink(tenant, namespace, sinkConfig, null);
Defensive patterns

Strategy: validation

Validate before calling

if (newCfg.getRetainOrdering() != null
    && !newCfg.getRetainOrdering().equals(existing.getRetainOrdering())) {
    throw new IllegalArgumentException("retainOrdering is immutable");
}

Try / catch

try {
    admin.sinks().updateSink(tenant, namespace, cfg, null);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Retain Ordering cannot be altered")) { /* revert retainOrdering */ }
    else throw e;
}

Prevention

When it happens

Trigger: Updating a sink with getRetainOrdering() non-null and not equal to the deployed value, e.g. turning ordering on for an existing unordered sink.

Common situations: Developer enables retainOrdering to fix out-of-order processing via update instead of recreate; shared config template sets retainOrdering: true while the deployed sink was created without it.

Related errors


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