{"record":{"id":"a07e0a3567c5eb4f","repo":"apache/cassandra","slug":"this-cluster-is-migrating-to-cluster-metadata-can-a07e0a","errorCode":null,"errorMessage":"This cluster is migrating to cluster metadata, can't move until that is done.","messagePattern":"This cluster is migrating to cluster metadata, can't move until that is done\\.","errorType":"exception","errorClass":"java.lang.IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java","lineNumber":172,"sourceCode":"                                                                  LeaveStreams.Kind.REMOVENODE));\n        InProgressSequences.finishInProgressSequences(toRemove);\n        Gossiper.instance.unsafeBroadcastLeftStatus(endpoint, tokens, metadata.directory.allJoinedEndpoints());\n    }\n\n    static void abortRemoveNode(String nodeId)\n    {\n        abortHelper(nodeId, MultiStepOperation.Kind.REMOVE, null);\n    }\n\n    /**\n     * move the node to new token or find a new token to boot to according to load\n     *\n     * @param newToken new token to boot to, or if null, find balanced token to boot to\n     */\n    static void move(Token newToken)\n    {\n        if (ClusterMetadataService.instance().isMigrating() || ClusterMetadataService.state() == ClusterMetadataService.State.GOSSIP)\n            throw new IllegalStateException(\"This cluster is migrating to cluster metadata, can't move until that is done.\");\n\n        if (newToken == null)\n            throw new IllegalArgumentException(\"Can't move to the undefined (null) token.\");\n\n        if (ClusterMetadata.current().tokenMap.tokens().contains(newToken))\n            throw new IllegalArgumentException(String.format(\"target token %s is already owned by another node.\", newToken));\n\n        // address of the current node\n        ClusterMetadata metadata = ClusterMetadata.current();\n        NodeId self = metadata.myNodeId();\n        // This doesn't make any sense in a vnodes environment.\n        if (metadata.tokenMap.tokens(self).size() > 1)\n        {\n            logger.error(\"Invalid request to move(Token); This node has more than one token and cannot be moved thusly.\");\n            throw new UnsupportedOperationException(\"This node has more than one token and cannot be moved thusly.\");\n        }\n\n        ClusterMetadataService.instance().commit(new PrepareMove(self,","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java#L154-L190","documentation":"SingleNodeSequences.move rejects moving a token while the cluster is still migrating from gossip-based metadata to Cluster Metadata (TCM). During migration (isMigrating() true or state == GOSSIP) topology operations are blocked to avoid divergent metadata, so an IllegalStateException is thrown.","triggerScenarios":"Running `nodetool move <newToken>` while ClusterMetadataService.instance().isMigrating() is true or ClusterMetadataService.state() is State.GOSSIP - i.e. during or after a failed in-place upgrade to TCM before migration completes.","commonSituations":"Upgrading a cluster to the Cassandra TCM releases and issuing topology commands before migration finishes; a node still running in GOSSIP state after a rolling restart; migration stuck halfway due to a failed node.","solutions":["Wait until the TCM migration completes (all nodes report ClusterMetadata state, not GOSSIP) and re-run the move.","Check `nodetool clustermetadata` / logs to see which nodes have not completed migration and restart/upgrade them.","If migration is stuck, resolve the underlying cause (down peer, failed bootstrap) per upgrade docs, then retry.","Defer the token move until after the upgrade window; it is not allowed mid-migration."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (ClusterMetadataService.instance().isMigrating() || ClusterMetadataService.state() == ClusterMetadataService.State.GOSSIP)\n    return; // defer move until migration completes","typeGuard":null,"tryCatchPattern":"try { SingleNodeSequences.move(token); }\ncatch (IllegalStateException e) { if (e.getMessage().contains(\"migrating\")) { scheduleRetryAfterMigration(); } else throw e; }","preventionTips":["Gate all topology commands on migration completion during upgrades","Monitor ClusterMetadataService.state() in automation before issuing moves","Move tokens only in maintenance windows after upgrades finish"],"tags":["cassandra","cluster-metadata","migration","topology"],"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"}