apache/druid · error · ISE
can't start.
Error message
can't start.
What it means
CatalogUpdateReceiver.start() uses a LifecycleLock; if canStart() returns false the receiver is in a state where starting is illegal (already started, stopped, or shut down), so it throws ISE("can't start."). This is Druid's standard lifecycle guard against double start or start-after-stop.
Source
Thrown at extensions-core/druid-catalog/src/main/java/org/apache/druid/catalog/sync/CatalogUpdateReceiver.java:69
private final LifecycleLock lifecycleLock = new LifecycleLock();
private final ScheduledExecutorService exec;
@Inject
public CatalogUpdateReceiver(
final CachedMetadataCatalog cachedCatalog,
final CatalogClientConfig clientConfig
)
{
this.cachedCatalog = cachedCatalog;
this.config = clientConfig;
this.exec = Execs.scheduledSingleThreaded("CatalogUpdateReceiver-Exec--%d");
}
@LifecycleStart
public void start()
{
if (!lifecycleLock.canStart()) {
throw new ISE("can't start.");
}
try {
final Duration delay = new Duration(config.getPollingPeriod());
final long maxDelayFuzz = config.getMaxRandomDelay();
ScheduledExecutors.scheduleWithFixedDelay(
exec,
delay,
delay,
() -> {
try {
long randomDelay = ThreadLocalRandom.current().nextLong(0, maxDelayFuzz);
Thread.sleep(randomDelay);
LOG.debug("Scheduled catalog refresh running");
resync();
LOG.debug("Scheduled catalog refresh is done");
}
catch (Throwable t) {View on GitHub (pinned to 9b90983fd2)
Solutions
- Start the receiver once via a Lifecycle (addHandler) instead of manual invocation so state transitions are managed.
- Ensure stop() completes (awaitStopped) before attempting start again, or construct a new receiver instance.
- Guard test code so it doesn't start the receiver both directly and through the lifecycle.
- Catch ISE and treat it as 'already started' if idempotent start is desired.
Example fix
// before
receiver.start();
receiver.start(); // ISE: can't start.
// after
if (!started) {
receiver.start();
started = true;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
receiver.start();
} catch (ISE e) {
if ("can't start.".equals(e.getMessage())) {
// already started or stopped; ignore or log
}
} Prevention
- Manage start/stop through a Lifecycle instead of manual calls.
- Call start exactly once per receiver instance.
- In tests, avoid double-starting via both direct call and lifecycle hook.
- Await stop completion before restarting.
When it happens
Trigger: Calling start() twice on the same receiver; calling start() after stop() or after lifecycle shutdown was initiated; concurrent start/stop from different threads without lifecycle coordination.
Common situations: Test code manually invoking start() alongside a Lifecycle that also starts it; restarting a service component without recreating the receiver; race between two threads calling start().
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/9ee724db98ad6e93.
Report an issue: GitHub.