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
- Call setInstance() only once per JVM; guard initialization with a check of ClusterMetadataService.instance.
- In tests, use the framework's cluster-per-JVM or classloader isolation mechanisms.
- 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
- Initialize CMS exactly once per JVM
- Use classloader/process isolation for multi-cluster test setups
- Check instance nullity before calling setInstance
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
- Addresses differ: !=
- Can only initialize cluster identifier during epoch
- Can't commit transformations when running in gossip mode…
- Can't finish migration, initiator=
- Can't ignore local host
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)