alibaba/canal · error · CanalException

{} isn't start , please check

Error message

{} isn't start , please check

What it means

Thrown by AbstractCanalLifeCycle.stop() when the component's `running` flag is false. This is the counterpart to the double-start guard — it prevents stopping a component that was never started or was already stopped. The `running` flag is volatile for cross-thread visibility.

Source

Thrown at common/src/main/java/com/alibaba/otter/canal/common/AbstractCanalLifeCycle.java:27

public abstract class AbstractCanalLifeCycle implements CanalLifeCycle {

    protected volatile boolean running = false; // 是否处于运行中

    public boolean isStart() {
        return running;
    }

    public void start() {
        if (running) {
            throw new CanalException(this.getClass().getName() + " has startup , don't repeat start");
        }

        running = true;
    }

    public void stop() {
        if (!running) {
            throw new CanalException(this.getClass().getName() + " isn't start , please check");
        }

        running = false;
    }

}

View on GitHub (pinned to 87be50e876)

Solutions

  1. Check `component.isStart()` before calling `component.stop()`.
  2. In finally/cleanup blocks, guard stop() with an isStart() check to handle cases where start() never succeeded.
  3. Track lifecycle state explicitly in application code to avoid redundant stop() calls.
  4. Swallow CanalException in shutdown hooks if a clean stop is best-effort.

Example fix

// before — unconditional stop in finally
try {
    canalServer.start();
} finally {
    canalServer.stop(); // throws if start() failed
}

// after — guarded stop
try {
    canalServer.start();
} finally {
    if (canalServer.isStart()) {
        canalServer.stop();
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Guard double-stop
if (component.isStart()) {
    component.stop();
} else {
    logger.info("{} is not running, skip stop", component.getClass().getSimpleName());
}

Type guard

null

Try / catch

// In finally/cleanup blocks, swallow the 'isn't start' exception gracefully
try {
    if (component.isStart()) {
        component.stop();
    }
} catch (CanalException e) {
    if (e.getMessage() != null && e.getMessage().contains("isn't start")) {
        logger.debug("Component already stopped: {}", component.getClass().getName());
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling stop() on a CanalLifeCycle component that is not currently running — e.g. calling canalServer.stop() after it was already stopped, or calling stop() on a component whose start() failed or was never invoked.

Common situations: Shutdown hook fires after a failed start (component never reached running state); double-stop in cleanup code; stop() called on a component that threw during start(); application error handling calls stop() unconditionally in a finally block.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/2856fb600f0bb07b. Report an issue: GitHub.