{"record":{"id":"2856fb600f0bb07b","repo":"alibaba/canal","slug":"isn-t-start-please-check","errorCode":null,"errorMessage":"{} isn't start , please check","messagePattern":"(.+?) isn't start , please check","errorType":"exception","errorClass":"CanalException","httpStatus":null,"severity":"error","filePath":"common/src/main/java/com/alibaba/otter/canal/common/AbstractCanalLifeCycle.java","lineNumber":27,"sourceCode":"public abstract class AbstractCanalLifeCycle implements CanalLifeCycle {\n\n    protected volatile boolean running = false; // 是否处于运行中\n\n    public boolean isStart() {\n        return running;\n    }\n\n    public void start() {\n        if (running) {\n            throw new CanalException(this.getClass().getName() + \" has startup , don't repeat start\");\n        }\n\n        running = true;\n    }\n\n    public void stop() {\n        if (!running) {\n            throw new CanalException(this.getClass().getName() + \" isn't start , please check\");\n        }\n\n        running = false;\n    }\n\n}\n","sourceCodeStart":9,"sourceCodeEnd":34,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/common/src/main/java/com/alibaba/otter/canal/common/AbstractCanalLifeCycle.java#L9-L34","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check `component.isStart()` before calling `component.stop()`.","In finally/cleanup blocks, guard stop() with an isStart() check to handle cases where start() never succeeded.","Track lifecycle state explicitly in application code to avoid redundant stop() calls.","Swallow CanalException in shutdown hooks if a clean stop is best-effort."],"exampleFix":"// before — unconditional stop in finally\ntry {\n    canalServer.start();\n} finally {\n    canalServer.stop(); // throws if start() failed\n}\n\n// after — guarded stop\ntry {\n    canalServer.start();\n} finally {\n    if (canalServer.isStart()) {\n        canalServer.stop();\n    }\n}","handlingStrategy":"validation","validationCode":"// Guard double-stop\nif (component.isStart()) {\n    component.stop();\n} else {\n    logger.info(\"{} is not running, skip stop\", component.getClass().getSimpleName());\n}","typeGuard":"null","tryCatchPattern":"// In finally/cleanup blocks, swallow the 'isn't start' exception gracefully\ntry {\n    if (component.isStart()) {\n        component.stop();\n    }\n} catch (CanalException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"isn't start\")) {\n        logger.debug(\"Component already stopped: {}\", component.getClass().getName());\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always check isStart() before calling stop().","In finally blocks, guard stop() calls since start() may not have succeeded.","Use idempotent shutdown procedures that tolerate being called when already stopped."],"tags":["lifecycle","double-stop","canal-common","state-management"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}