{"record":{"id":"04649d2c3384ecde","repo":"alibaba/spring-ai-alibaba","slug":"at-least-one-limit-must-be-specified-threadlimit","errorCode":null,"errorMessage":"At least one limit must be specified (threadLimit or runLimit)","messagePattern":"At least one limit must be specified \\(threadLimit or runLimit\\)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/modelcalllimit/ModelCallLimitHook.java","lineNumber":169,"sourceCode":"\n\t\tpublic Builder threadLimit(Integer threadLimit) {\n\t\t\tthis.threadLimit = threadLimit;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder runLimit(Integer runLimit) {\n\t\t\tthis.runLimit = runLimit;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder exitBehavior(ExitBehavior exitBehavior) {\n\t\t\tthis.exitBehavior = exitBehavior;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ModelCallLimitHook build() {\n\t\t\tif (threadLimit == null && runLimit == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"At least one limit must be specified (threadLimit or runLimit)\");\n\t\t\t}\n\t\t\treturn new ModelCallLimitHook(this);\n\t\t}\n\t}\n}\n","sourceCodeStart":151,"sourceCodeEnd":175,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/modelcalllimit/ModelCallLimitHook.java#L151-L175","documentation":"ModelCallLimitHook.Builder.build() validates that at least one of threadLimit or runLimit is set. Building a hook with both null would produce a hook that can never trigger, so IllegalArgumentException is thrown at construction time.","triggerScenarios":"Calling ModelCallLimitHook.builder().build() (optionally after setting only exitBehavior or other non-limit options) without calling threadLimit(...) or runLimit(...).","commonSituations":"Copy-pasting builder code and deleting the limit lines; conditionally setting limits but having both branches skipped; refactoring where limits moved to config that failed to load.","solutions":["Call .threadLimit(n) and/or .runLimit(n) on the builder before build().","If limits come from configuration, validate the config values are non-null before building the hook.","Skip registering the hook entirely when no limit is configured, instead of building an empty one."],"exampleFix":"// before\nModelCallLimitHook hook = ModelCallLimitHook.builder().exitBehavior(ExitBehavior.ERROR).build();\n// after\nModelCallLimitHook hook = ModelCallLimitHook.builder().threadLimit(20).exitBehavior(ExitBehavior.ERROR).build();","handlingStrategy":"validation","validationCode":"if (threadLimit == null && runLimit == null) {\n    throw new IllegalStateException(\"Configure threadLimit or runLimit before building ModelCallLimitHook\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never build the hook without at least one limit","Wire limits from config with explicit non-null checks","Add builder usage to a shared factory method that enforces limits"],"tags":["java","builder-validation","missing-required-argument","model-call-limit"],"backgroundTag":"missing-required-argument","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"}