apache/pulsar · error · IllegalArgumentException

Retain Ordering cannot be altered

Error message

Retain Ordering cannot be altered

What it means

FunctionConfigUtils.validateUpdate enforces that certain function settings are immutable after creation. retainOrdering changes would require a new subscription/consumption mode on the existing topic, so Pulsar rejects the update with IllegalArgumentException instead of applying it.

Source

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

        }
        if (!StringUtils.isEmpty(newConfig.getOutputSerdeClassName()) && !newConfig.getOutputSerdeClassName()
                .equals(existingConfig.getOutputSerdeClassName())) {
            throw new IllegalArgumentException("Output Serde mismatch");
        }
        if (!StringUtils.isEmpty(newConfig.getOutputSchemaType()) && !newConfig.getOutputSchemaType()
                .equals(existingConfig.getOutputSchemaType())) {
            throw new IllegalArgumentException("Output Schema mismatch");
        }
        if (!StringUtils.isEmpty(newConfig.getLogTopic())) {
            mergedConfig.setLogTopic(newConfig.getLogTopic());
        }
        if (newConfig.getProcessingGuarantees() != null && !newConfig.getProcessingGuarantees()
                .equals(existingConfig.getProcessingGuarantees())) {
            throw new IllegalArgumentException("Processing Guarantees cannot be altered");
        }
        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");
        }
        if (!StringUtils.isEmpty(newConfig.getOutput())) {
            mergedConfig.setOutput(newConfig.getOutput());
        }
        if (newConfig.getUserConfig() != null) {
            mergedConfig.setUserConfig(newConfig.getUserConfig());
        }
        if (newConfig.getSecrets() != null) {
            mergedConfig.setSecrets(newConfig.getSecrets());
        }
        if (newConfig.getRuntime() != null && !newConfig.getRuntime().equals(existingConfig.getRuntime())) {
            throw new IllegalArgumentException("Runtime cannot be altered");
        }
        @SuppressWarnings("deprecation")

View on GitHub (pinned to 820761864e)

Solutions

  1. Keep retainOrdering unchanged in the update request (pass null or the existing value fetched via getFunction).
  2. If a different retainOrdering is truly needed, delete and re-create (or re-register) the function with the new value.
  3. Diff the new config against the current config before submitting to catch the conflicting field.
  4. Check client SDK/tooling defaults so retainOrdering is not implicitly set on updates.

Example fix

// before
FunctionConfig update = existing;
update.setRetainOrdering(!existing.getRetainOrdering()); // rejected
admin.functions().updateFunction(update, configFile);
// after
FunctionConfig update = existing;
update.setRetainOrdering(existing.getRetainOrdering()); // unchanged, or leave null
admin.functions().updateFunction(update, configFile);
Defensive patterns

Strategy: validation

Validate before calling

FunctionConfig existing = admin.functions().getFunction(tenant, namespace, fnName);
if (update.getRetainOrdering() != null && !update.getRetainOrdering().equals(existing.getRetainOrdering())) {
    update.setRetainOrdering(null); // drop immutable change
}

Try / catch

try {
    admin.functions().updateFunction(update, pkgFile);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("cannot be altered")) { /* reset immutable fields and retry once */ }
    else throw e;
}

Prevention

When it happens

Trigger: Calling updateFunction (or the PUT admin API) with a FunctionConfig whose getRetainOrdering() is non-null and differs from the existing function's retainOrdering.

Common situations: Submitting an updated function config where the caller populated retainOrdering=true by default or changed it to fix ordering issues; client tooling that always serializes all fields, turning an unset field into an explicit conflicting value.

Related errors


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