{"record":{"id":"3f6a1ee8ebbcaec4","repo":"apache/cassandra","slug":"can-not-resume-bootstrap-as-join-sequence-has-not","errorCode":null,"errorMessage":"Can not resume bootstrap as join sequence has not been started","messagePattern":"Can not resume bootstrap as join sequence has not been started","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/StorageService.java","lineNumber":1038,"sourceCode":"                throw new IllegalStateException(\"Cannot join the ring until bootstrap completes\");\n            }\n        }\n        else if (isBootstrapMode())\n        {\n            // bootstrap is not complete hence node cannot join the ring\n            logger.warn(\"Can't join the ring because bootstrap hasn't completed.\");\n            throw new IllegalStateException(\"Cannot join the ring until bootstrap completes\");\n        }\n    }\n\n    public void resumeBootstrapSequence()\n    {\n        ClusterMetadata metadata = ClusterMetadata.current();\n        NodeId id = metadata.myNodeId();\n        MultiStepOperation<?> sequence = metadata.inProgressSequences.get(id);\n\n        if (!(sequence instanceof BootstrapAndJoin) && !(sequence instanceof BootstrapAndReplace))\n            throw new IllegalStateException(\"Can not resume bootstrap as join sequence has not been started\");\n        clearOngoingBootstrap();\n        InProgressSequences.finishInProgressSequences(id);\n        // only start transports and report that we're done if the resumed bootstrap completed successfully\n        // See CASSANDRA-16491\n        if (ongoingBootstrap.get() == null)\n        {\n            if (!isNativeTransportRunning())\n                daemon.initializeClientTransports();\n            daemon.start();\n            progressSupport.progress(\"bootstrap\", new ProgressEvent(ProgressEventType.COMPLETE, 1, 1, \"Resume bootstrap complete\"));\n            logger.info(\"Resume complete\");\n        }\n    }\n\n    public boolean readyToFinishJoiningRing()\n    {\n        ClusterMetadata metadata = ClusterMetadata.current();\n        NodeId id = metadata.myNodeId();","sourceCodeStart":1020,"sourceCodeEnd":1056,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/StorageService.java#L1020-L1056","documentation":"Thrown by StorageService.resumeBootstrap when the node's current in-progress sequence in ClusterMetadata is neither a BootstrapAndJoin nor a BootstrapAndReplace. It means resumeBootstrap was invoked, but no bootstrap/join sequence was ever started (or it already finished and was cleaned up), so there is nothing to resume. Cassandra requires an actual in-flight multi-step operation persisted in ClusterMetadata before a bootstrap can be resumed.","triggerScenarios":"Calling nodetool resumebootstrap (or StorageService.resumeBootstrap()) on a node whose ClusterMetadata.inProgressSequences contains no BootstrapAndJoin/BootstrapAndReplace entry for its own NodeId — e.g. the node never started bootstrapping, bootstrap already completed, the sequence was cancelled, or the node was restarted after the sequence was removed.","commonSituations":"Operator runs resumebootstrap on a node that isn't actually stuck mid-bootstrap; running it on an already-bootstrapped node by mistake; running it after a failed bootstrap was cancelled and cleaned up; cluster metadata was reset or the node was decommissioned/re-added.","solutions":["Verify the node actually has an in-flight bootstrap: check nodetool metadata / logs for a BootstrapAndJoin or BootstrapAndReplace sequence before calling resumebootstrap","If bootstrap never started, just start the node normally (or run the bootstrap/replacement again) instead of resuming","If the sequence is stuck in a bad state, cancel it (nodetool cancelhandoff / cancel the bootstrap per docs) and re-bootstrap from scratch","If bootstrap already completed, no action is needed — the node is already joined; skip the resume step"],"exampleFix":"// before: blindly resuming\nstorageService.resumeBootstrap();\n\n// after: only resume when a bootstrap sequence exists\nClusterMetadata md = ClusterMetadata.current();\nMultiStepOperation<?> seq = md.inProgressSequences.get(md.myNodeId());\nif (seq instanceof BootstrapAndJoin || seq instanceof BootstrapAndReplace)\n    storageService.resumeBootstrap();","handlingStrategy":"validation","validationCode":"ClusterMetadata md = ClusterMetadata.current();\nMultiStepOperation<?> seq = md.inProgressSequences.get(md.myNodeId());\nboolean canResume = seq instanceof BootstrapAndJoin || seq instanceof BootstrapAndReplace;\nif (!canResume) throw new IllegalStateException(\"No bootstrap sequence in progress; start bootstrap instead of resuming\");","typeGuard":"static boolean isBootstrapSequence(MultiStepOperation<?> seq) {\n    return seq instanceof BootstrapAndJoin || seq instanceof BootstrapAndReplace;\n}","tryCatchPattern":"try {\n    storageService.resumeBootstrap();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"join sequence has not been started\")) {\n        logger.warn(\"Nothing to resume; node is not mid-bootstrap\", e);\n    } else throw e;\n}","preventionTips":["Check nodetool metadata / logs for an in-progress bootstrap sequence before calling resumebootstrap","Only use resumebootstrap after an interrupted bootstrap, not as a general join command","Automate the check: gate resume calls on the presence of BootstrapAndJoin/BootstrapAndReplace in ClusterMetadata"],"tags":["bootstrap","cluster-join","illegal-state","cassandra"],"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-14T11:17:12.474Z"}