apache/pulsar · error · IllegalArgumentException

Subscription Name cannot be altered

Error message

Subscription Name cannot be altered

What it means

Thrown by SinkConfigUtils.validateUpdate when sourceSubscriptionName is set in the new config and differs from the existing one. The Pulsar subscription a sink consumes from is fixed after creation, so changing it is rejected to avoid silently switching the consumption source.

Source

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

    public static SinkConfig validateUpdate(SinkConfig existingConfig, SinkConfig newConfig) {
        SinkConfig mergedConfig = clone(existingConfig);

        if (!existingConfig.getTenant().equals(newConfig.getTenant())) {
            throw new IllegalArgumentException("Tenants differ");
        }
        if (!existingConfig.getNamespace().equals(newConfig.getNamespace())) {
            throw new IllegalArgumentException("Namespaces differ");
        }
        if (!existingConfig.getName().equals(newConfig.getName())) {
            throw new IllegalArgumentException("Sink Names differ");
        }
        if (!StringUtils.isEmpty(newConfig.getClassName())) {
            mergedConfig.setClassName(newConfig.getClassName());
        }
        if (!StringUtils.isEmpty(newConfig.getSourceSubscriptionName()) && !newConfig.getSourceSubscriptionName()
                .equals(existingConfig.getSourceSubscriptionName())) {
            throw new IllegalArgumentException("Subscription Name cannot be altered");
        }

        if (newConfig.getInputSpecs() == null) {
            newConfig.setInputSpecs(new HashMap<>());
        }

        if (mergedConfig.getInputSpecs() == null) {
            mergedConfig.setInputSpecs(new HashMap<>());
        }
        if (!StringUtils.isEmpty(newConfig.getLogTopic())) {
            mergedConfig.setLogTopic(newConfig.getLogTopic());
        }

        if (newConfig.getInputs() != null) {
            newConfig.getInputs().forEach((topicName -> {
                newConfig.getInputSpecs().putIfAbsent(topicName,
                        ConsumerConfig.builder().isRegexPattern(false).build());
            }));

View on GitHub (pinned to 820761864e)

Solutions

  1. Set sourceSubscriptionName in the update config to the same value as the existing sink (or leave it empty to keep it)
  2. If a different subscription is required, delete and re-create the sink with the new subscription name
  3. Check the current value via 'pulsar-admin sinks get' before updating

Example fix

// before
sinkConfig.setSourceSubscriptionName("new-sub");
admin.sinks().updateSink(tenant, namespace, sinkConfig, null);
// after
sinkConfig.setSourceSubscriptionName("original-sub"); // must match existing
admin.sinks().updateSink(tenant, namespace, sinkConfig, null);
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try {
    admin.sinks().updateSink(tenant, namespace, cfg, null);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Subscription Name cannot be altered")) { /* revert to existing subscription */ }
    else throw e;
}

Prevention

When it happens

Trigger: Updating a sink while passing a non-empty getSourceSubscriptionName() that does not equal the existing config's subscription name (including changing it from a configured value to a different one).

Common situations: Teams rename subscriptions in shared config; a sink created with subscription 'sub-a' is updated with 'sub-b'; user tries to point an existing sink at a different topic subscription instead of recreating it.

Related errors


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