{"record":{"id":"3da7dddf17071b26","repo":"elastic/elasticsearch","slug":"ran-out-of-nodes-to-take-to-the-next-version","errorCode":null,"errorMessage":"Ran out of nodes to take to the next version","messagePattern":"Ran out of nodes to take to the next version","errorType":"exception","errorClass":"TestClustersException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java","lineNumber":491,"sourceCode":"            }\n        }\n    }\n\n    @Override\n    public void restart() {\n        nodes.forEach(ElasticsearchNode::restart);\n    }\n\n    public void goToNextVersion() {\n        stop(false);\n        nodes.all(ElasticsearchNode::goToNextVersion);\n        start();\n        writeUnicastHostsFiles();\n    }\n\n    public void nextNodeToNextVersion() {\n        if (nodeIndex + 1 > nodes.size()) {\n            throw new TestClustersException(\"Ran out of nodes to take to the next version\");\n        }\n        ElasticsearchNode node = nodes.getByName(clusterName + \"-\" + nodeIndex);\n        node.stop(false);\n        node.goToNextVersion();\n        commonNodeConfig();\n        nodeIndex += 1;\n        if (node.getTestDistribution().equals(TestDistribution.DEFAULT)) {\n            if (hasDeprecationIndexing(node)) {\n                node.setting(\"cluster.deprecation_indexing.enabled\", \"false\");\n            }\n        }\n        node.start();\n    }\n\n    private static boolean hasDeprecationIndexing(ElasticsearchNode node) {\n        return node.getVersion().onOrAfter(\"7.16.0\") && node.getSettingKeys().contains(\"stateless.enabled\") == false;\n    }\n","sourceCodeStart":473,"sourceCodeEnd":509,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java#L473-L509","documentation":"Thrown by ElasticsearchCluster.nextNodeToNextVersion when nodeIndex + 1 exceeds the cluster's node count. nextNodeToNextVersion rolls nodes one at a time to the next version during rolling-upgrade BWC tests; once every node has been advanced, there are no more to advance.","triggerScenarios":"Calling nextNodeToNextVersion() more times than there are nodes in the cluster (each call increments nodeIndex and stops at nodes.size()).","commonSituations":"A rolling-upgrade test loop iterates one step too many, or the node count was reduced after the loop bound was computed. Also when an upgrade test assumes N nodes but the cluster has fewer.","solutions":["Guard the loop: `while (cluster.nodeIndex < cluster.nodes.size()) cluster.nextNodeToNextVersion()`.","Ensure the cluster's numberOfNodes matches the number of rolling steps the test performs.","If using nextNodeToNextVersion in a parameterized test, cap iterations at nodes.size()."],"exampleFix":"// before\nfor (int i = 0; i < 5; i++) {\n  cluster.nextNodeToNextVersion();  // throws when i exceeds node count\n}\n\n// after\nwhile (cluster.getNodes().size() > 0 && cluster.nodeIndex < cluster.getNodes().size()) {\n  cluster.nextNodeToNextVersion();\n}","handlingStrategy":"validation","validationCode":"while (cluster.getNodes().size() > 0\n       && cluster.nodeIndex < cluster.getNodes().size()) {\n    cluster.nextNodeToNextVersion();\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Cap rolling iterations at the node count, not a hardcoded constant.","Sync the cluster's numberOfNodes with the test's expected rolling steps.","Add an assertion before the loop: nodeCount == rollingStepCount."],"tags":["test","testclusters","bwc","rolling-upgrade","validation"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}