{"record":{"id":"4b72e45db2f79f29","repo":"alibaba/spring-ai-alibaba","slug":"maxtools-must-be-0","errorCode":null,"errorMessage":"maxTools must be > 0","messagePattern":"maxTools must be > 0","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/toolselection/ToolSelectionInterceptor.java","lineNumber":222,"sourceCode":"\tpublic static class Builder {\n\t\tprivate ChatModel selectionModel;\n\t\tprivate String systemPrompt = DEFAULT_SYSTEM_PROMPT;\n\t\tprivate Integer maxTools;\n\t\tprivate Set<String> alwaysInclude;\n\n\t\tpublic Builder selectionModel(ChatModel selectionModel) {\n\t\t\tthis.selectionModel = selectionModel;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder systemPrompt(String systemPrompt) {\n\t\t\tthis.systemPrompt = systemPrompt;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder maxTools(int maxTools) {\n\t\t\tif (maxTools <= 0) {\n\t\t\t\tthrow new IllegalArgumentException(\"maxTools must be > 0\");\n\t\t\t}\n\t\t\tthis.maxTools = maxTools;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder alwaysInclude(Set<String> alwaysInclude) {\n\t\t\tthis.alwaysInclude = alwaysInclude;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder alwaysInclude(String... toolNames) {\n\t\t\tthis.alwaysInclude = new HashSet<>(Arrays.asList(toolNames));\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ToolSelectionInterceptor build() {\n\t\t\tif (selectionModel == null) {\n\t\t\t\tthrow new IllegalStateException(\"selectionModel is required\");","sourceCodeStart":204,"sourceCodeEnd":240,"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/toolselection/ToolSelectionInterceptor.java#L204-L240","documentation":"ToolSelectionInterceptor.Builder.maxTools(int) requires a strictly positive value because it caps how many tools the selection model may pick; a zero or negative cap would make tool selection always produce nothing. IllegalArgumentException is thrown when maxTools <= 0.","triggerScenarios":"Calling ToolSelectionInterceptor.Builder.maxTools(0) or maxTools(negative).","commonSituations":"maxTools read from config with a default of 0 meaning 'unset'; arithmetic that computes the cap (e.g. remaining budget) reaching 0 or below.","solutions":["Pass a positive integer, e.g. maxTools(3)","Guard the value before building: only call maxTools when the value is > 0, otherwise leave the default","If 0 means 'no limit', skip setting maxTools entirely instead of passing 0"],"exampleFix":"// before\nToolSelectionInterceptor.builder().maxTools(0).build();\n// after\nToolSelectionInterceptor.builder().maxTools(3).build();","handlingStrategy":"validation","validationCode":"if (maxTools <= 0) throw new IllegalArgumentException(\"maxTools must be a positive integer\");","typeGuard":"boolean isValidToolCap(int n) { return n > 0; }","tryCatchPattern":"try { b.maxTools(n); } catch (IllegalArgumentException e) { log.warn(\"Ignoring invalid maxTools={}\", n); }","preventionTips":["Validate config values before passing them to builders","Skip calling maxTools() to keep the sensible default instead of passing 0","Clamp with Math.max(1, value) when deriving the cap dynamically"],"tags":["java","validation","builder","tool-selection"],"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"}