apache/cassandra · error · IllegalStateException

Cluster metadata is already initialized to

Error message

Cluster metadata is already initialized to %s.

What it means

ClusterMetadataService is a process-wide singleton. setInstance() throws IllegalStateException (with a 'Previously initialized trace' attached as cause on the first call path) if it is called when a CMS instance has already been installed, to prevent silently swapping cluster metadata backends at runtime.

Solutions

  1. Call setInstance() only once per JVM; guard initialization with a check of ClusterMetadataService.instance.
  2. In tests, use the framework's cluster-per-JVM or classloader isolation mechanisms.
  3. If re-initialization is intended, restructure to a fresh process/classloader rather than reassigning the singleton.

Example fix

// before
ClusterMetadataService.setInstance(newCms); // second call -> IllegalStateException
// after
if (ClusterMetadataService.instance() == null)
    ClusterMetadataService.setInstance(newCms);
Defensive patterns

Strategy: validation

Validate before calling

if (ClusterMetadataService.instance() != null) {
    logger.info("CMS already initialized; skipping setInstance");
    return;
}

Try / catch

try {
    ClusterMetadataService.setInstance(newCms);
} catch (IllegalStateException e) {
    logger.warn("CMS already set; keeping existing instance", e);
}

Prevention

When it happens

Trigger: Calling ClusterMetadataService.setInstance(newCms) more than once in the same JVM — e.g. tests initializing multiple clusters, or a node re-running initialization after startup.

Common situations: In-JVM dtest frameworks creating several clusters without resetting the singleton, or code paths that re-invoke startup/initialization logic on restart within the same process.

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/9eba85fe2b978df4. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/tcm/ClusterMetadataService.java:117

import static org.apache.cassandra.tcm.ClusterMetadataService.State.GOSSIP;
import static org.apache.cassandra.tcm.ClusterMetadataService.State.LOCAL;
import static org.apache.cassandra.tcm.ClusterMetadataService.State.REMOTE;
import static org.apache.cassandra.tcm.ClusterMetadataService.State.RESET;
import static org.apache.cassandra.tcm.compatibility.GossipHelper.emptyWithSchemaFromSystemTables;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.Collectors3.toImmutableSet;

public class ClusterMetadataService
{
    private static final Logger logger = LoggerFactory.getLogger(ClusterMetadataService.class);

    private static ClusterMetadataService instance;
    private static Throwable trace;

    public static void setInstance(ClusterMetadataService newInstance)
    {
        if (instance != null)
            throw new IllegalStateException(String.format("Cluster metadata is already initialized to %s.", instance),
                                            trace);
        instance = newInstance;
        RegistrationStatus.instance.onInitialized();
        if (newInstance.metadata().myNodeId() != NodeId.UNREGISTERED)
            RegistrationStatus.instance.onRegistration();
        trace = new RuntimeException("Previously initialized trace");
        DatabaseDescriptor.applyLocator();
    }

    @VisibleForTesting
    public static ClusterMetadataService unsetInstance()
    {
        ClusterMetadataService tmp = instance();
        RegistrationStatus.instance.resetState();
        instance = null;
        return tmp;
    }

View on GitHub (pinned to 88fd0f6a0e)