{"record":{"id":"991303595dfcae35","repo":"alibaba/spring-ai-alibaba","slug":"maxattempts-must-be-1","errorCode":null,"errorMessage":"maxAttempts must be >= 1","messagePattern":"maxAttempts must be >= 1","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/toolretry/ToolRetryInterceptor.java","lineNumber":181,"sourceCode":"\tpublic static class Builder {\n\t\t// Total number of attempts including the first call.\n\t\tprivate int maxAttempts = 3;\n\t\tprivate Set<String> toolNames;\n\t\tprivate Predicate<Exception> retryOn = e -> true; // Retry on all exceptions by default\n\t\tprivate OnFailureBehavior onFailure = OnFailureBehavior.RETURN_MESSAGE;\n\t\tprivate Function<Exception, String> errorFormatter;\n\t\tprivate double backoffFactor = 2.0;\n\t\tprivate long initialDelayMs = 1000;\n\t\tprivate long maxDelayMs = 60000;\n\t\tprivate boolean jitter = true;\n\n\t\t/**\n\t\t * Set the maximum number of attempts, including the first call. Must be >= 1.\n\t\t * @param maxAttempts total attempts (initial call plus retries)\n\t\t */\n\t\tpublic Builder maxAttempts(int maxAttempts) {\n\t\t\tif (maxAttempts < 1) {\n\t\t\t\tthrow new IllegalArgumentException(\"maxAttempts must be >= 1\");\n\t\t\t}\n\t\t\tthis.maxAttempts = maxAttempts;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the maximum number of retries (excluding the first call).\n\t\t * @param maxRetries number of retries; total attempts will be {@code maxRetries + 1}\n\t\t * @deprecated use {@link #maxAttempts(int)} instead, which counts the initial call\n\t\t */\n\t\t@Deprecated\n\t\tpublic Builder maxRetries(int maxRetries) {\n\t\t\tif (maxRetries < 0) {\n\t\t\t\tthrow new IllegalArgumentException(\"maxRetries must be >= 0\");\n\t\t\t}\n\t\t\tthis.maxAttempts = maxRetries + 1;\n\t\t\treturn this;\n\t\t}","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/toolretry/ToolRetryInterceptor.java#L163-L199","documentation":"ToolRetryInterceptor.Builder.maxAttempts() requires at least 1, since the count includes the initial call; 0 or negative would mean the tool is never called. Values < 1 throw IllegalArgumentException immediately.","triggerScenarios":"Calling Builder.maxAttempts(0) or a negative int, often from config where maxAttempts was set to 0 meaning \"no retries\" by the author.","commonSituations":"Config value of 0 intended as \"disable retries\" (correct value is 1); off-by-one confusion between attempts and retries; unset config defaulting to 0.","solutions":["Pass maxAttempts >= 1 (use 1 to disable retries entirely)","Convert \"retry count\" config (0 = no retries) to attempts = retries + 1","Guard/default the config value before building"],"exampleFix":"// before\nToolRetryInterceptor.builder().maxAttempts(0) // meant: no retries\n// after\nToolRetryInterceptor.builder().maxAttempts(1) // 1 = initial call only, no retries","handlingStrategy":"validation","validationCode":"int attempts = Math.max(1, configuredRetries + 1);\nToolRetryInterceptor.builder().maxAttempts(attempts).build();","typeGuard":"boolean isValidMaxAttempts(int n) { return n >= 1; }","tryCatchPattern":"try { builder.maxAttempts(v); } catch (IllegalArgumentException e) { log.warn(\"Bad maxAttempts '{}', using 1\", v); builder.maxAttempts(1); }","preventionTips":["Remember attempts includes the first call; retries = attempts - 1","Convert retry-count configs with + 1","Default unset config to 1 instead of 0"],"tags":["config","retry","validation","builder"],"backgroundTag":"invalid-argument-value","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}