apache/pulsar · error · RuntimeException

The log error handler cannot be changed once the appender is

Error message

The log error handler cannot be changed once the appender is started

What it means

LogAppender is a lifecycle-managed component: once start() has created the log-topic Producer (state == STARTED), swapping the ErrorHandler would leave the producer's internal failure callbacks pointing at the stale handler. setHandler therefore rejects the change with RuntimeException until stop() is called.

Source

Thrown at pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/instance/LogAppender.java:95

    }

    @Override
    public boolean ignoreExceptions() {
        return false;
    }

    @Override
    public ErrorHandler getHandler() {
        return errorHandler;
    }

    @Override
    public void setHandler(ErrorHandler errorHandler) {
        if (errorHandler == null) {
            throw new RuntimeException("The log error handler cannot be set to null");
        }
        if (isStarted()) {
            throw new RuntimeException("The log error handler cannot be changed once the appender is started");
        }
        this.errorHandler = errorHandler;
    }

    @Override
    public State getState() {
        return state;
    }

    @Override
    public void initialize() {
        this.state = State.INITIALIZED;
    }

    @Override
    public void start() {
        this.state = State.STARTING;
        try {

View on GitHub (pinned to 820761864e)

Solutions

  1. Call stop() first, then setHandler(), then start() again to apply a new handler
  2. Set the handler immediately after constructing the LogAppender and before any start() call
  3. Restructure code so the ErrorHandler is created once with mutable internals (e.g. delegate to an AtomicReference) if dynamic behavior is needed

Example fix

// before
appender.start();
appender.setHandler(newHandler); // throws
// after
appender.setHandler(newHandler);
appender.start();
Defensive patterns

Strategy: validation

Validate before calling

if (logAppender.getState() == State.STARTED) {
    logAppender.stop();
}
logAppender.setHandler(newHandler);
logAppender.start();

Try / catch

try { logAppender.setHandler(newHandler); }
catch (RuntimeException e) {
  if (e.getMessage().contains("cannot be changed once the appender is started")) {
    log.error("stop() the appender before swapping handlers", e);
  }
}

Prevention

When it happens

Trigger: Calling setHandler after start() on the same LogAppender instance; runtime code or user harness attempting to hot-swap error handling while the appender is running.

Common situations: Reconfiguring logging at runtime in a long-lived function instance; test code reusing an appender across cases without stopping it; framework upgrade path calling setHandler during restart logic while the producer is still open.

Related errors


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