{"record":{"id":"f4f001c13780e7bb","repo":"apache/cassandra","slug":"can-t-upgrade-from-gossip-since-cms-is-already-ini","errorCode":null,"errorMessage":"Can't upgrade from gossip since CMS is already initialized","messagePattern":"Can't upgrade from gossip since CMS is already initialized","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/ClusterMetadataService.java","lineNumber":495,"sourceCode":"        }\n\n        if (existingMembers.isEmpty())\n        {\n            logger.info(\"First CMS node\");\n            Set<InetAddressAndPort> candidates = metadata\n                                                 .directory\n                                                 .allJoinedEndpoints()\n                                                 .stream()\n                                                 .filter(ep -> !FBUtilities.getBroadcastAddressAndPort().equals(ep) &&\n                                                               !ignored.contains(ep))\n                                                 .collect(toImmutableSet());\n\n            Election.instance.nominateSelf(candidates, ignored, metadata, true);\n            ClusterMetadataService.instance().triggerSnapshot();\n        }\n        else\n        {\n            throw new IllegalStateException(\"Can't upgrade from gossip since CMS is already initialized\");\n        }\n    }\n\n    public void reconfigureCMS(ReplicationParams replicationParams)\n    {\n        ClusterMetadata metadata = ClusterMetadata.current();\n        Set<NodeId> downNodes = new HashSet<>();\n        for (InetAddressAndPort ep : metadata.directory.allJoinedEndpoints())\n            if (!FailureDetector.instance.isAlive(ep))\n                downNodes.add(metadata.directory.peerId(ep));\n        PrepareCMSReconfiguration.Complex transformation = new PrepareCMSReconfiguration.Complex(replicationParams, downNodes);\n        transformation.verify(metadata);\n\n        ClusterMetadataService.instance()\n                              .commit(transformation);\n\n        InProgressSequences.finishInProgressSequences(ReconfigureCMS.SequenceKey.instance);\n    }","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/ClusterMetadataService.java#L477-L513","documentation":"upgradeFromGossip only makes sense when the cluster is still running in gossip mode with an uninitialized CMS. If the CMS/election is already initialized (a CMS has been placed and epochs advanced past EMPTY), the method refuses with IllegalStateException, because migrating from gossip again would overwrite or conflict with the established TCM state.","triggerScenarios":"Calling upgradeFromGossip() a second time, or on a cluster where TCM was already enabled — detected by existing CMS members (existingMembers non-empty / CMS state no longer GOSSIP) after Election.instance.nominateSelf was previously executed.","commonSituations":"Upgrade automation re-run after a partial/failed run that already nominated a CMS; operator unsure whether the upgrade already happened and re-invokes the command; a cluster that was already migrated later being restored with a gossip-era script.","solutions":["Verify CMS state first; if the CMS is already initialized, no upgrade is needed — skip upgradeFromGossip.","Check logs / ClusterMetadata epochs to confirm the previous upgrade completed successfully instead of re-running.","If a previous run half-finished, resolve the actual CMS state (which node owns the CMS) rather than forcing a re-upgrade.","Guard the automation: only call upgradeFromGossip when the service state is GOSSIP."],"exampleFix":"// before\nservice.upgradeFromGossip(ignored); // may run again on already-migrated cluster\n// after\nif (ClusterMetadataService.instance().state() == CMSState.GOSSIP)\n    service.upgradeFromGossip(ignored);","handlingStrategy":"validation","validationCode":"if (ClusterMetadataService.instance().state() != CMSState.GOSSIP) skipUpgrade();","typeGuard":"boolean needsGossipUpgrade = ClusterMetadataService.instance().state() == CMSState.GOSSIP;","tryCatchPattern":"try { service.upgradeFromGossip(ignored); } catch (IllegalStateException e) { logger.info(\"CMS already initialized, skipping upgrade\"); }","preventionTips":["Make upgrade scripts idempotent by checking CMS state first","Log migration completion so operators know it already ran","Only invoke upgradeFromGossip once per cluster"],"tags":["upgrade","cluster-metadata","idempotency","illegal-state"],"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-14T16:17:12.679Z"}