apache/cassandra · error · IllegalStateException

Cannot join the ring until bootstrap completes

Error message

Cannot join the ring until bootstrap completes

What it means

A node started in write_survey mode (join_ring=false with bootstrap) collects writes without owning token ranges. joinRing() from this mode is only allowed once bootstrap has completed; otherwise an IllegalStateException is thrown because the node cannot safely take ownership of its range yet.

Solutions

  1. Wait for bootstrap to complete (watch `nodetool netstats` / bootstrap logs) before calling joinRing
  2. Verify SystemKeyspace.bootstrapComplete() is true before invoking joinRing
  3. If the node is stuck, fix the failing bootstrap (streaming errors) and retry
  4. Restart the node normally if bootstrap was never intended

Example fix

// before
ss.joinRing();
// after
if (SystemKeyspace.bootstrapComplete())
    ss.joinRing();
Defensive patterns

Strategy: validation

Validate before calling

if (!SystemKeyspace.bootstrapComplete()) skipJoinRing();

Try / catch

try { ss.joinRing(); } catch (IllegalStateException e) { /* wait for bootstrap, retry later */ }

Prevention

When it happens

Trigger: Calling StorageService.joinRing() (JMX or API) while isSurveyMode is true and SystemKeyspace.bootstrapComplete() is false.

Common situations: Operator starting a node with -Dcassandra.join_ring=false then calling joinRing before streaming/bootstrap finished; automation that joins the ring too early.

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/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/eea55e3801fe1f99. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/service/StorageService.java:1008

            }
        }
        else if (isSurveyMode)
        {
            // if isSurveyMode then verify the node is in the right state to join the ring
            // or that it has already done so
            if (ClusterMetadata.current().myNodeState() == JOINED)
            {
                // note: this has always been a no-op, starting a previously joined node in
                // survey mode is meaningless as bootstrapping and joining the ring is already
                // complete and a full joined node being restarted in survey mode does not prevent
                // it participating in reads. This exists only for backwards compatibilty.
                logger.info("Leaving write survey mode and joining ring at operator request");
                isSurveyMode = false;
            }
            else if (!SystemKeyspace.bootstrapComplete())
            {
                logger.warn("Can't join the ring because in write_survey mode and bootstrap hasn't completed");
                throw new IllegalStateException("Cannot join the ring until bootstrap completes");
            }
            else if (readyToFinishJoiningRing())
            {
                logger.info("Leaving write survey mode and joining ring at operator request");
                exitWriteSurveyMode();
                isSurveyMode = false;
                daemon.start();
            }
            else
            {
                logger.warn("Can't join the ring because in write_survey mode and bootstrap hasn't completed");
                throw new IllegalStateException("Cannot join the ring until bootstrap completes");
            }
        }
        else if (isBootstrapMode())
        {
            // bootstrap is not complete hence node cannot join the ring
            logger.warn("Can't join the ring because bootstrap hasn't completed.");

View on GitHub (pinned to 88fd0f6a0e)