{"record":{"id":"cde899a1ba8a9cd5","repo":"elastic/elasticsearch","slug":"nodes-must-not-be-null-or-empty","errorCode":null,"errorMessage":"nodes must not be null or empty","messagePattern":"nodes must not be null or empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"client/rest/src/main/java/org/elasticsearch/client/RestClient.java","lineNumber":234,"sourceCode":"            throw new IllegalArgumentException(\"hosts must not be null nor empty\");\n        }\n        List<Node> nodes = Arrays.stream(hosts).map(Node::new).collect(Collectors.toList());\n        return new RestClientBuilder(nodes);\n    }\n\n    /**\n     * Get the underlying HTTP client.\n     */\n    public HttpAsyncClient getHttpClient() {\n        return this.client;\n    }\n\n    /**\n     * Replaces the nodes with which the client communicates.\n     */\n    public synchronized void setNodes(Collection<Node> nodes) {\n        if (nodes == null || nodes.isEmpty()) {\n            throw new IllegalArgumentException(\"nodes must not be null or empty\");\n        }\n        AuthCache authCache = new BasicAuthCache();\n\n        Map<HttpHost, Node> nodesByHost = new LinkedHashMap<>();\n        for (Node node : nodes) {\n            Objects.requireNonNull(node, \"node cannot be null\");\n            // TODO should we throw an IAE if we have two nodes with the same host?\n            nodesByHost.put(node.getHost(), node);\n            authCache.put(node.getHost(), new BasicScheme());\n        }\n        this.nodeTuple = new NodeTuple<>(Collections.unmodifiableList(new ArrayList<>(nodesByHost.values())), authCache);\n        this.blacklist.clear();\n    }\n\n    /**\n     * Get the list of nodes that the client knows about. The list is\n     * unmodifiable.\n     */","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/client/rest/src/main/java/org/elasticsearch/client/RestClient.java#L216-L252","documentation":"RestClient.setNodes(Collection<Node>) replaces the live node set used for routing and requires a non-null, non-empty collection. Rejecting empty prevents the client from entering a state where no host is routable (which would make every subsequent request fail with a less obvious error).","triggerScenarios":"Calling setNodes with an empty list after a nodes-info refresh that returned zero eligible nodes; passing null from a discovery layer that failed; a filter that excluded all nodes.","commonSituations":"Sniff/node-retrieval returning empty due to a transient cluster outage; a NodeSelector filtering out every node; race where setNodes is called before discovery completes.","solutions":["Guard the refresh: only call setNodes when the discovered collection is non-empty, otherwise keep the previous node set and log a warning.","Fix the upstream discovery/selector to return at least one node.","Retry discovery with backoff before replacing nodes."],"exampleFix":"// before\nclient.setNodes(discoveredNodes); // discoveredNodes empty after sniff\n// after\nif (discoveredNodes == null || discoveredNodes.isEmpty()) {\n    logger.warn(\"node refresh returned no nodes; keeping previous set\");\n} else {\n    client.setNodes(discoveredNodes);\n}","handlingStrategy":"validation","validationCode":"if (discoveredNodes == null || discoveredNodes.isEmpty()) {\n    logger.warn(\"node refresh returned no nodes; retaining previous node set\");\n} else {\n    client.setNodes(discoveredNodes);\n}","typeGuard":null,"tryCatchPattern":"try { client.setNodes(discoveredNodes); } catch (IllegalArgumentException e) { /* keep previous nodes; retry discovery later */ }","preventionTips":["Never call setNodes with an empty collection; keep the prior set when discovery is empty.","Investigate recurring empty-discovery results — they indicate a cluster or selector problem.","Log every skipped refresh so silent degradation is observable."],"tags":["rest-client","validation","config","node-routing"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}