apache/pulsar · error · IllegalArgumentException

Only one of retain ordering or retain key ordering can be se

Error message

Only one of retain ordering or retain key ordering can be set

What it means

SinkConfigUtils validates conflicting ordering options: both retain-ordering and retain-key-ordering were enabled (or the combination with effectively-once processing guarantees is invalid), but only one ordering mode may be set.

Source

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

                }
                if (consumerSpec.getMessagePayloadProcessorConfig() != null) {
                    ValidatorUtils.validateMessagePayloadProcessor(consumerSpec.getMessagePayloadProcessorConfig(),
                            inputFunction.getTypePool());
                }
            }
        }

        if (sinkConfig.getRetainKeyOrdering() != null
                && sinkConfig.getRetainKeyOrdering()
                && sinkConfig.getProcessingGuarantees() != null
                && sinkConfig.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.EFFECTIVELY_ONCE) {
            throw new IllegalArgumentException(
                    "When effectively once processing guarantee is specified, retain Key ordering cannot be set");
        }

        if (sinkConfig.getRetainKeyOrdering() != null && sinkConfig.getRetainKeyOrdering()
                && sinkConfig.getRetainOrdering() != null && sinkConfig.getRetainOrdering()) {
            throw new IllegalArgumentException("Only one of retain ordering or retain key ordering can be set");
        }

        // validate user defined config if enabled and classloading is enabled
        if (validateConnectorConfig) {
            if (sinkFunction.isEnableClassloading()) {
                validateSinkConfig(sinkConfig, sinkFunction);
            } else {
                log.warn("Skipping annotation based validation of sink config as classloading is disabled");
            }
        }

        return new ExtractedSinkDetails(sinkClassName, typeArg.asErasure().getTypeName(), functionClassName);
    }

    public static Collection<String> collectAllInputTopics(SinkConfig sinkConfig) {
        List<String> retval = new LinkedList<>();
        if (sinkConfig.getInputs() != null) {
            retval.addAll(sinkConfig.getInputs());

View on GitHub (pinned to 820761864e)

Solutions

  1. Enable only one of retainOrdering or retainKeyOrdering

Example fix

// before
sinkConfig.setRetainOrdering(true);
sinkConfig.setRetainKeyOrdering(true);
// after
sinkConfig.setRetainOrdering(true);
sinkConfig.setRetainKeyOrdering(false);
Defensive patterns

Strategy: validation

Validate before calling

if (Boolean.TRUE.equals(config.getRetainKeyOrdering())
    && Boolean.TRUE.equals(config.getRetainOrdering())) {
  throw new IllegalStateException("Set only one of retainOrdering / retainKeyOrdering");
}

Try / catch

try {
  admin.sinks().createSink(config, archive);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().contains("retain ordering or retain key ordering")) {
    config.setRetainKeyOrdering(false); // keep global ordering
  } else { throw e; }
}

Prevention

When it happens

Trigger: sinkConfig.retainKeyOrdering == true AND sinkConfig.retainOrdering == true during validateAndExtractDetails.

Common situations: Merging config templates that each set one flag; misunderstanding the semantics and setting both hoping for 'more ordering'.

Related errors


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