{"record":{"id":"f59c68c54d21919e","repo":"elastic/elasticsearch","slug":"can-t-treat-as-single-node-as-it-has-nodes","errorCode":null,"errorMessage":"Can't treat {} as single node as it has {} nodes","messagePattern":"Can't treat (.+?) as single node as it has (.+?) nodes","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java","lineNumber":638,"sourceCode":"    @Internal\n    public List<String> getLeakMessages() {\n        return nodes.stream().map(ElasticsearchNode::getLeakMessage).filter(Objects::nonNull).toList();\n    }\n\n    @Override\n    public void setNameCustomization(Function<String, String> nameCustomization) {\n        nodes.all(each -> each.setNameCustomization(nameCustomization));\n    }\n\n    @Override\n    @Internal\n    public boolean isProcessAlive() {\n        return nodes.stream().noneMatch(node -> node.isProcessAlive() == false);\n    }\n\n    public ElasticsearchNode singleNode() {\n        if (nodes.size() != 1) {\n            throw new IllegalStateException(\"Can't treat \" + this + \" as single node as it has \" + nodes.size() + \" nodes\");\n        }\n        return getFirstNode();\n    }\n\n    private void addWaitForClusterHealth() {\n        waitConditions.put(\"cluster health yellow\", (node) -> {\n            try {\n                boolean httpSslEnabled = getFirstNode().isHttpSslEnabled();\n                WaitForHttpResource wait = new WaitForHttpResource(\n                    httpSslEnabled ? \"https\" : \"http\",\n                    getFirstNode().getHttpSocketURI(),\n                    nodes.size()\n                );\n                if (httpSslEnabled) {\n                    getFirstNode().configureHttpWait(wait);\n                }\n                List<Map<String, String>> credentials = getFirstNode().getCredentials();\n                if (getFirstNode().getCredentials().isEmpty() == false) {","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java#L620-L656","documentation":"Thrown by ElasticsearchCluster.singleNode() when the cluster does not have exactly one node. singleNode() returns the sole ElasticsearchNode for single-node convenience APIs; calling it on a multi-node (or zero-node) cluster is ambiguous and rejected.","triggerScenarios":"Calling cluster.singleNode() when nodes.size() != 1 — either before any nodes are added, or after configuring a multi-node cluster.","commonSituations":"A test helper assumes a single-node cluster but the test cluster was configured with numberOfNodes > 1, or singleNode() is called early before node sizing has completed.","solutions":["Ensure the cluster is configured with numberOfNodes = 1 before calling singleNode().","If multi-node, iterate getNodes() instead of calling singleNode().","Add a guard at test entry: assumeTrue(cluster.getNodes().size() == 1) when the test requires single-node semantics."],"exampleFix":"// before\ntestClusters { cluster { numberOfNodes = 3 } }\nElasticsearchNode n = testClusters.cluster.singleNode();  // throws\n\n// after (single-node)\ntestClusters { cluster { numberOfNodes = 1 } }\nElasticsearchNode n = testClusters.cluster.singleNode();\n\n// after (multi-node)\ntestClusters.cluster.getNodes().forEach(n -> { ... });","handlingStrategy":"validation","validationCode":"if (cluster.getNodes().size() != 1) {\n    throw new IllegalStateException(\"singleNode() requires exactly 1 node, has \" + cluster.getNodes().size());\n}\nElasticsearchNode n = cluster.singleNode();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reserve singleNode() for numberOfNodes = 1 clusters.","Iterate getNodes() for multi-node clusters.","Add assumeTrue(cluster.getNodes().size() == 1) in single-node test helpers."],"tags":["test","testclusters","validation","api-misuse"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}