apache/pulsar · error · PulsarServerException
Cannot start the service once it was stopped
Error message
Cannot start the service once it was stopped
What it means
PulsarService.start() acquires a mutex and checks the internal lifecycle State before initializing the broker. The service may only be started once from State.Init; after shutdown() moves the state past Init, calling start() again on the same PulsarService instance throws PulsarServerException. Instances are single-use: to restart, create a new PulsarService.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java:871
/**
* Start the pulsar service instance.
*/
public void start() throws PulsarServerException {
log.info()
.attr("version", (brokerVersion != null ? brokerVersion : "unknown"))
.attr("gitRevision", PulsarVersion.getGitSha())
.attr("gitBranch", PulsarVersion.getGitBranch())
.attr("buildUser", PulsarVersion.getBuildUser())
.attr("buildHost", PulsarVersion.getBuildHost())
.attr("buildTime", PulsarVersion.getBuildTime())
.log("Starting Pulsar Broker service");
long startTimestamp = System.currentTimeMillis(); // start time mills
mutex.lock();
try {
if (state != State.Init) {
throw new PulsarServerException("Cannot start the service once it was stopped");
}
if (config.getWebServicePort().isEmpty()
&& config.getWebServicePortTls().isEmpty()
&& BindAddressValidator.validateBindAddresses(config, Arrays.asList("http", "https")).isEmpty()) {
throw new IllegalArgumentException(
"webServicePort/webServicePortTls or http/https bindAddresses must be present");
}
if (config.isAuthorizationEnabled() && !config.isAuthenticationEnabled()) {
throw new IllegalStateException("Invalid broker configuration. Authentication must be enabled with "
+ "authenticationEnabled=true when authorization is enabled with authorizationEnabled=true.");
}
if (config.getDefaultRetentionSizeInMB() > 0
&& config.getBacklogQuotaDefaultLimitBytes() > 0
&& config.getBacklogQuotaDefaultLimitBytes()
>= (config.getDefaultRetentionSizeInMB() * 1024L * 1024L)) {View on GitHub (pinned to 820761864e)
Solutions
- Create a new PulsarService instance (with the same configuration) and call start() on it instead of restarting the old one.
- Ensure shutdown() is terminal in your code path: after calling close()/shutdown(), discard the reference rather than calling start() again.
- Guard restart logic: track whether the instance was already started/stopped, and rebuild the service on restart.
- If using PulsarService in tests, wrap setup in a fresh instance per test class/method (or use the testcontainers/mock runners) instead of reusing a stopped instance.
Example fix
// before pulsarService.shutdown(); pulsarService.start(); // throws: state != Init // after pulsarService.shutdown(); PulsarService pulsarService2 = new PulsarService(config); pulsarService2.start();
Defensive patterns
Strategy: try-catch
Validate before calling
// Track lifecycle yourself; PulsarService state is not publicly queryable.
if (startedOnce && !recreateServiceOnRestart) {
throw new IllegalStateException("PulsarService is single-use; create a new instance to restart");
} Try / catch
try {
pulsarService.start();
} catch (PulsarServerException e) {
if (e.getMessage().contains("Cannot start the service once it was stopped")) {
pulsarService = new PulsarService(config); // rebuild instead of restart
pulsarService.start();
} else {
throw e;
}
} Prevention
- Treat PulsarService as non-restartable: one start() per instance; rebuild for restarts.
- In lifecycle managers, implement restart() as close() + new PulsarService(config).start().
- Avoid calling start() from multiple threads; the internal mutex serializes but does not make restart legal.
- In tests, create a fresh service per test lifecycle hook instead of reusing stopped instances.
When it happens
Trigger: Calling pulsar.start() on a PulsarService instance whose state != State.Init — typically after a previous start()/shutdown() (state moved to Closed/Stopped), or calling start() twice concurrently/sequentially on the same instance, or a framework (test harness, Spring lifecycle) restarting the same bean after stop.
Common situations: Unit/integration tests that stop the broker then try to restart the same PulsarService object; application code implementing custom restart logic by re-calling start() after an error; lifecycle managers (e.g., Spring @PostConstruct/@PreDestroy, Kafka-style restart handlers) treating PulsarService as restartable.
Related errors
- ManagedLedgerFactory is already closed.
- ManagedLedger ${name} has already been closed
- webServicePort/webServicePortTls or http/https bindAddresses
- The retention size must > the backlog quota limit size, but
- The retention time must > the backlog quota limit time, but
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/25ad99efaad9b96f.
Report an issue: GitHub.