apache/pulsar · error · IllegalArgumentException

Namespaces differ

Error message

Namespaces differ

What it means

Update-compatibility check in SinkConfigUtils.validateUpdate: the existing and new sink configurations live in different namespaces, which would relocate the sink; sink updates must preserve tenant/namespace/name identity, so the differing namespace fields are rejected.

Source

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

            retval.addAll(sinkConfig.getInputSpecs().keySet());
        }
        return retval;
    }

    @SneakyThrows
    public static SinkConfig clone(SinkConfig sinkConfig) {
        return ObjectMapperFactory.getMapper().reader().readValue(
                ObjectMapperFactory.getMapper().writer().writeValueAsBytes(sinkConfig), SinkConfig.class);
    }

    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<>());

View on GitHub (pinned to 820761864e)

Solutions

  1. Keep the namespace unchanged; recreate the sink elsewhere if needed

Example fix

// before
newConfig.setNamespace("ns2");
SinkConfig merged = SinkConfigUtils.validateUpdate(existing, newConfig);
// after
newConfig.setNamespace(existing.getNamespace());
SinkConfig merged = SinkConfigUtils.validateUpdate(existing, newConfig);
Defensive patterns

Strategy: validation

Validate before calling

if (!existing.getNamespace().equals(newConfig.getNamespace())) {
  throw new IllegalStateException("Update cannot change namespace; delete+create instead");
}

Try / catch

try {
  admin.sinks().updateSink(tenant, ns, name, newConfig);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().equals("Namespaces differ")) {
    // recreate sink in target namespace via createSink
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling validateUpdate with a newConfig whose getNamespace() differs from existingConfig.getNamespace().

Common situations: Copying a sink config template across namespaces; admin tooling that rebuilds config from request body where namespace field was changed; trying to 'move' a sink via update.

Related errors


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