{"record":{"id":"0c46974625ce21bc","repo":"elastic/elasticsearch","slug":"maxattempts-must-be-1-but-was","errorCode":null,"errorMessage":"maxAttempts must be >= 1 but was [{}]","messagePattern":"maxAttempts must be >= 1 but was \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/util/HttpUtils.java","lineNumber":33,"sourceCode":"public final class HttpUtils {\n\n    private static final int HTTP_READ_MAX_ATTEMPTS = 3;\n    private static final long HTTP_READ_RETRY_BACKOFF_MILLIS = 1000L;\n\n    private HttpUtils() {}\n\n    @FunctionalInterface\n    public interface Sleeper {\n        void sleep(long millis) throws InterruptedException;\n    }\n\n    public static byte[] readHttpBytesWithRetry(String url) throws IOException {\n        return readHttpBytesWithRetry(url, HTTP_READ_MAX_ATTEMPTS, HTTP_READ_RETRY_BACKOFF_MILLIS, Thread::sleep);\n    }\n\n    public static byte[] readHttpBytesWithRetry(String url, int maxAttempts, long baseBackoffMillis, Sleeper sleeper) throws IOException {\n        if (maxAttempts <= 0) {\n            throw new IllegalArgumentException(\"maxAttempts must be >= 1 but was [\" + maxAttempts + \"]\");\n        }\n        if (baseBackoffMillis < 0) {\n            throw new IllegalArgumentException(\"baseBackoffMillis must be >= 0 but was [\" + baseBackoffMillis + \"]\");\n        }\n\n        IOException lastException = null;\n        for (int attempt = 1; attempt <= maxAttempts; attempt++) {\n            if (attempt > 1 && baseBackoffMillis > 0) {\n                long backoff = baseBackoffMillis * (attempt - 1);\n                try {\n                    sleeper.sleep(backoff);\n                } catch (InterruptedException e) {\n                    Thread.currentThread().interrupt();\n                    throw new IOException(\"Interrupted while retrying download from: \" + url, e);\n                }\n            }\n\n            try (InputStream in = URI.create(url).toURL().openStream()) {","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/util/HttpUtils.java#L15-L51","documentation":"HttpUtils.readHttpBytesWithRetry validates its maxAttempts argument up front. The retry loop iterates attempt = 1..maxAttempts, so maxAttempts <= 0 would mean zero attempts and silent fall-through; the guard rejects non-positive values with the actual invalid value embedded.","triggerScenarios":"Calling readHttpBytesWithRetry(url, maxAttempts, ...) with maxAttempts = 0 or negative, typically via a computed/passed retry count from configuration that resolved to zero.","commonSituations":"A Gradle build property or env var for HTTP retry count defaulting to 0 when unset; passing a literal 0 during testing; arithmetic that underflows to <= 0.","solutions":["Pass maxAttempts >= 1; use the built-in default HTTP_READ_MAX_ATTEMPTS when unsure.","Clamp configured values: Math.max(1, configuredAttempts) before calling.","If the 0 came from a missing config, fix the config resolution default rather than masking it."],"exampleFix":"// before\nHttpUtils.readHttpBytesWithRetry(url, configuredAttempts, backoff, sleeper); // configuredAttempts=0\n// after\nHttpUtils.readHttpBytesWithRetry(url, Math.max(1, configuredAttempts), backoff, sleeper);","handlingStrategy":"validation","validationCode":"int safeAttempts = Math.max(1, configuredAttempts);\nHttpUtils.readHttpBytesWithRetry(url, safeAttempts, backoff, sleeper);","typeGuard":null,"tryCatchPattern":"try { HttpUtils.readHttpBytesWithRetry(url, attempts, backoff, sleeper); } catch (IllegalArgumentException e) { /* log misconfig and fall back to defaults */ HttpUtils.readHttpBytesWithRetry(url); }","preventionTips":["Default to the no-arg overload readHttpBytesWithRetry(url) when you don't need custom retry tuning.","Validate retry config at load time and fail fast with a clear message.","Clamp externally supplied retry counts to >= 1."],"tags":["validation","http","build","preconditions"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}