apache/pulsar · error · IllegalArgumentException

Runtime cannot be altered

Error message

Runtime cannot be altered

What it means

A function's runtime (JAVA, PYTHON, GO, etc.) is fixed at creation time. validateUpdate throws IllegalArgumentException if an update attempts to change it, since the worker's runtime machinery cannot be swapped in place for a running function.

Source

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

        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")
        boolean autoAckChanged = newConfig.getAutoAck() != null
                && !newConfig.getAutoAck().equals(existingConfig.getAutoAck());
        if (autoAckChanged) {
            throw new IllegalArgumentException("AutoAck cannot be altered");
        }
        if (newConfig.getMaxMessageRetries() != null) {
            mergedConfig.setMaxMessageRetries(newConfig.getMaxMessageRetries());
        }
        if (!StringUtils.isEmpty(newConfig.getDeadLetterTopic())) {
            mergedConfig.setDeadLetterTopic(newConfig.getDeadLetterTopic());
        }
        if (!StringUtils.isEmpty(newConfig.getSubName()) && !newConfig.getSubName()
                .equals(existingConfig.getSubName())) {
            throw new IllegalArgumentException("Subscription Name cannot be altered");
        }
        if (newConfig.getParallelism() != null) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Do not set runtime (or set it to the existing value) when updating.
  2. Delete and re-create the function with the new runtime to switch languages.
  3. Verify the code path building the update config isn't picking up a foreign runtime value.

Example fix

// before
update.setRuntime(FunctionConfig.Runtime.PYTHON); // existing is JAVA -> thrown
// after
admin.functions().deleteFunction(tenant, namespace, name);
admin.functions().createFunction(newConfigWithPythonRuntime, pkgFile);
Defensive patterns

Strategy: validation

Validate before calling

FunctionConfig existing = admin.functions().getFunction(tenant, namespace, fnName);
if (update.getRuntime() != null && !update.getRuntime().equals(existing.getRuntime())) {
    throw new IllegalStateException("Runtime is immutable; delete and re-create the function");
}

Try / catch

try {
    admin.functions().updateFunction(update, pkgFile);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Runtime cannot be altered")) { /* fall back to delete+create */ }
    else throw e;
}

Prevention

When it happens

Trigger: Calling updateFunction with a FunctionConfig whose getRuntime() is non-null and differs from existingConfig.getRuntime().

Common situations: Rewriting a Java function in Python and trying to update in place; update code that copies runtime from a different config object; tooling that sets runtime explicitly on every submit.

Related errors


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