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

  1. Only call stop() if start() succeeded; track start state or use try/finally mirroring start.
  2. Manage the receiver through a Lifecycle (addHandler) so start/stop pairing is enforced.
  3. Catch ISE and treat as 'already stopped' in shutdown paths where idempotency is needed.
  4. 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

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.