{"record":{"id":"394b130097f2263a","repo":"apache/cassandra","slug":"cannot-drain-node-did-it-already-happen","errorCode":null,"errorMessage":"Cannot drain node (did it already happen?)","messagePattern":"Cannot drain node \\(did it already happen\\?\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/java/org/apache/cassandra/service/StorageService.java","lineNumber":3888,"sourceCode":"    public String getDrainProgress()\n    {\n        return String.format(\"Drained %s/%s ColumnFamilies\", remainingCFs, totalCFs);\n    }\n\n    /**\n     * Shuts node off to writes, empties memtables and the commit log.\n     */\n    public synchronized void drain() throws IOException, InterruptedException, ExecutionException\n    {\n        drain(false);\n    }\n\n    protected synchronized void drain(boolean isFinalShutdown) throws IOException, InterruptedException, ExecutionException\n    {\n        if (Stage.areMutationExecutorsTerminated())\n        {\n            if (!isFinalShutdown)\n                logger.warn(\"Cannot drain node (did it already happen?)\");\n            return;\n        }\n\n        assert !isShutdown;\n        isShutdown = true;\n\n        Throwable preShutdownHookThrowable = Throwables.perform(null, preShutdownHooks.stream().map(h -> h::run));\n        if (preShutdownHookThrowable != null)\n            logger.error(\"Attempting to continue draining after pre-shutdown hooks returned exception\", preShutdownHookThrowable);\n\n        try\n        {\n            String msg = \"starting drain process\";\n            if (!isFinalShutdown)\n                logger.info(msg);\n            else\n                logger.debug(msg);\n            transientMode = Optional.of(Mode.DRAINING);","sourceCodeStart":3870,"sourceCodeEnd":3906,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/StorageService.java#L3870-L3906","documentation":"Drain was requested but the mutation executors are already terminated, meaning the node was already drained (or shut down). The method just logs a warning and returns - the 'error' is benign and defensive against double-drain.","triggerScenarios":"Calling nodetool 'drain' twice; calling drain after 'nodetool stop'/JVM shutdown already drained; a shutdown hook running drain while a user also invoked drain.","commonSituations":"Automation scripts that retry drain after a timeout; operators running drain manually and then stopping the service, which drains again during shutdown.","solutions":["No action needed - the node is already drained","If scripted, check executor state or tolerate the warning and exit 0","Restart Cassandra to leave the drained state before further operations"],"exampleFix":"// before\nnodetool drain\nnodetool drain  # second call warns\n// after\nnodetool drain && echo already-or-just-drained","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (RuntimeException e) { if (e.getMessage().contains(\"Cannot drain node\")) { /* already drained - treat as success */ } }","preventionTips":["Make drain idempotent in scripts","Track drained state in orchestration tools","Only drain once per node lifecycle"],"tags":["shutdown","drain","idempotency"],"backgroundTag":"invalid-state-transition","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}