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
- Call stop() first, then setHandler(), then start() again to apply a new handler
- Set the handler immediately after constructing the LogAppender and before any start() call
- 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
- Set the handler once, immediately after construction and before start()
- Check getState() before mutating configuration
- If dynamic behavior is needed, use a delegating ErrorHandler with an AtomicReference
- Ensure stop() is called in teardown before reconfiguration
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
- The log error handler cannot be set to null
- Authentication has not completed
- Pulsar client has been closed, can not build LookupService w
- ServiceUrlProvider has already been initialized
- PulsarHttpClientFactory for ${clientInstanceId} is closed
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/839d52a7c0428690.
Report an issue: GitHub.