apache/druid · error · ISE
can't stop.
Error message
can't stop.
What it means
CatalogUpdateReceiver.stop() checks lifecycleLock.canStop(); if the receiver was never started, or was already stopped, stopping is illegal and it throws ISE("can't stop."). It protects the exec executor and lock state from inconsistent shutdown.
Source
Thrown at extensions-core/druid-catalog/src/main/java/org/apache/druid/catalog/sync/CatalogUpdateReceiver.java:112
LOG.info("Catalog update receiver started");
}
finally {
lifecycleLock.exitStart();
}
try {
resync();
}
catch (Throwable t) {
LOG.warn(t, "Failed to perform initial catalog synchronization");
}
}
@LifecycleStop
public void stop()
{
if (!lifecycleLock.canStop()) {
throw new ISE("can't stop.");
}
LOG.info("Catalog update receiver stopped");
exec.shutdownNow();
lifecycleLock.exitStop();
}
private void resync() throws Exception
{
RetryUtils.retry(
() -> {
cachedCatalog.resync();
return true;
},
e -> true,
config.getMaxSyncRetries()
);
}View on GitHub (pinned to 9b90983fd2)
Solutions
- Only call stop() if start() succeeded; track start state or use try/finally mirroring start.
- Manage the receiver through a Lifecycle (addHandler) so start/stop pairing is enforced.
- Catch ISE and treat as 'already stopped' in shutdown paths where idempotency is needed.
- Fix the earlier start failure that left the receiver never-started instead of suppressing at stop time.
Example fix
// before
receiver.stop(); // may throw if never started
// after
if (started) {
receiver.stop();
started = false;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
receiver.stop();
} catch (ISE e) {
if ("can't stop.".equals(e.getMessage())) {
// never started or already stopped; ignore or log
}
} Prevention
- Stop only after a successful start (track state or use try/finally).
- Use Lifecycle handlers so stop pairs with start.
- Investigate the start failure that left the receiver unstarted rather than swallowing stop errors.
- Make shutdown paths idempotent with caught ISE handling.
When it happens
Trigger: Calling stop() on a receiver that was never started; calling stop() twice; calling stop() after shutdown of the enclosing lifecycle.
Common situations: Teardown code that unconditionally calls stop() regardless of start success; tests stopping receivers in @AfterAll when start failed; double-close from both component and lifecycle.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/17470c538fe7791d.
Report an issue: GitHub.