{"record":{"id":"170e2378302ea7f3","repo":"spring-projects/spring-ai","slug":"tool-call-arguments-are-null-or-empty-for-tool","errorCode":null,"errorMessage":"Tool call arguments are null or empty for tool: ${toolName}. Using empty JSON object as default.","messagePattern":"Tool call arguments are null or empty for tool: (.+?)\\. Using empty JSON object as default\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-model/src/main/java/org/springframework/ai/model/tool/DefaultToolCallingManager.java","lineNumber":277,"sourceCode":"\t\t\t\t\t\t.conversationHistory(partialConversationHistory)\n\t\t\t\t\t\t.returnDirect(Objects.requireNonNullElse(returnDirect, false))\n\t\t\t\t\t\t.build();\n\t\t\t\t\tthrow new ToolCallLimitExceededException(limitBreach.toolName(), limitBreach.limit(),\n\t\t\t\t\t\t\tpartialToolExecutionResult);\n\t\t\t\t}\n\n\t\t\t\t// ToolCallLimitBehavior.RETURN_ERROR_RESPONSE: skip invoking this tool\n\t\t\t\t// call but keep processing the rest of the batch.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tString toolInputArguments = toolCall.arguments();\n\n\t\t\t// Handle the possible null parameter situation in streaming mode.\n\t\t\tfinal String finalToolInputArguments;\n\t\t\tif (!StringUtils.hasText(toolInputArguments)) {\n\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\tlogger.warn(\"Tool call arguments are null or empty for tool: \" + toolName\n\t\t\t\t\t\t\t+ \". Using empty JSON object as default.\");\n\t\t\t\t}\n\t\t\t\tfinalToolInputArguments = \"{}\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfinalToolInputArguments = toolInputArguments;\n\t\t\t}\n\n\t\t\tToolCallback toolCallback = toolCallbacks.stream()\n\t\t\t\t.filter(tool -> toolName.equals(tool.getToolDefinition().name()))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseGet(() -> this.resolutionFallbackEnabled ? this.toolCallbackResolver.resolve(toolName) : null);\n\n\t\t\tif (toolCallback == null) {\n\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\tlogger.warn(POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING_START + toolName\n\t\t\t\t\t\t\t+ POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING_END);\n\t\t\t\t}","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-model/src/main/java/org/springframework/ai/model/tool/DefaultToolCallingManager.java#L259-L295","documentation":"DefaultToolCallingManager logs this warning when a tool call's arguments string is null or empty, which commonly happens in streaming mode where the LLM may omit the arguments field. Instead of failing, the manager substitutes an empty JSON object \"{}\" and proceeds with tool execution. The tool then receives no arguments, so any required parameters will be missing.","triggerScenarios":"A streaming LLM response produces a ToolCall with arguments() returning null or blank; the tool call is executed via executeToolCall (invoked from internalToolExecutionResult).","commonSituations":"Streaming responses from models that omit the arguments field for parameterless tools; partial/empty argument chunks in streaming aggregation; models that emit a tool call without an arguments JSON block.","solutions":["If the tool has no parameters, ignore the warning — the \"{}\" default is correct.","If the tool requires parameters, check that the streaming response is fully aggregated before tool execution (ensure the arguments chunks are merged).","Verify the model/provider actually supports tool calling with arguments in streaming mode.","Inspect the raw model response to confirm the LLM is emitting arguments; adjust prompts or upgrade the model if it consistently omits them."],"exampleFix":"// before (tool requiring args, receives {})\n@Tool(description=\"Lookup weather\")\nString weather(@ToolParam(required = true) String city) { ... }\n\n// after (make the parameter optional / defaulted to survive empty args)\n@Tool(description=\"Lookup weather\")\nString weather(@ToolParam(required = false) String city) {\n    if (city == null || city.isBlank()) return \"Please provide a city\";\n    ...\n}","handlingStrategy":"type-guard","validationCode":"if (toolCall.arguments() == null || toolCall.arguments().isBlank()) {\n    logger.warn(\"Tool {} called without arguments; required params will be missing\", toolCall.name());\n}","typeGuard":"boolean hasArgs(ToolCall c) { return c.arguments() != null && !c.arguments().isBlank(); }","tryCatchPattern":null,"preventionTips":["Make @Tool parameters optional with defaults so empty {} won't break execution.","Verify streaming aggregation merges all argument chunks.","Prefer non-streaming for tools with required parameters.","Check provider support for arguments in streaming tool calls."],"tags":["tool-calling","streaming","json"],"backgroundTag":"empty-required-field","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}