{"record":{"id":"34d0206b64c6d061","repo":"spring-projects/spring-ai","slug":"currently-only-one-tool-call-is-supported-per-mess-34d020","errorCode":null,"errorMessage":"Currently only one tool call is supported per message!","messagePattern":"Currently only one tool call is supported per message!","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiStreamFunctionCallingHelper.java","lineNumber":131,"sourceCode":"\n\tprivate ChatCompletionMessage merge(ChatCompletionMessage previous, ChatCompletionMessage current) {\n\t\tvar content = (current.content() != null ? current.content()\n\t\t\t\t: (previous.content() != null) ? previous.content() : \"\");\n\t\tRole role = (current.role() != null ? current.role() : previous.role());\n\t\trole = (role != null ? role : Role.ASSISTANT); // default to ASSISTANT (if null\n\t\tString name = (current.name() != null ? current.name() : previous.name());\n\n\t\tList<ToolCall> toolCalls = new ArrayList<>();\n\t\tToolCall lastPreviousTooCall = null;\n\t\tif (previous.toolCalls() != null) {\n\t\t\tlastPreviousTooCall = previous.toolCalls().get(previous.toolCalls().size() - 1);\n\t\t\tif (previous.toolCalls().size() > 1) {\n\t\t\t\ttoolCalls.addAll(previous.toolCalls().subList(0, previous.toolCalls().size() - 1));\n\t\t\t}\n\t\t}\n\t\tif (current.toolCalls() != null) {\n\t\t\tif (current.toolCalls().size() > 1) {\n\t\t\t\tthrow new IllegalStateException(\"Currently only one tool call is supported per message!\");\n\t\t\t}\n\t\t\tvar currentToolCall = current.toolCalls().iterator().next();\n\t\t\tif (currentToolCall.id() != null) {\n\t\t\t\tif (lastPreviousTooCall != null) {\n\t\t\t\t\ttoolCalls.add(lastPreviousTooCall);\n\t\t\t\t}\n\t\t\t\ttoolCalls.add(currentToolCall);\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttoolCalls.add(merge(lastPreviousTooCall, currentToolCall));\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (lastPreviousTooCall != null) {\n\t\t\t\ttoolCalls.add(lastPreviousTooCall);\n\t\t\t}\n\t\t}\n\t\treturn new ChatCompletionMessage(content, role, name, toolCalls);","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiStreamFunctionCallingHelper.java#L113-L149","documentation":"MistralAiStreamFunctionCallingHelper.merge() combines consecutive streaming chunks of a tool-calling response. It supports at most one tool call per streamed message; if an incoming chunk's toolCalls list contains more than one entry, IllegalStateException is thrown because incremental merging of multiple parallel tool calls is not implemented.","triggerScenarios":"Streaming a Mistral chat completion where the model emits a message with two or more parallel tool calls in one chunk (e.g. the model decides to call two functions simultaneously).","commonSituations":"Prompts that expose multiple tools to Mistral and the model responds with parallel tool invocation; newer Mistral models with multi tool-call output combined with an older Spring AI stream helper.","solutions":["Upgrade spring-ai-mistral-ai to a version supporting multiple parallel tool calls.","Reduce exposed tools or adjust the prompt to discourage parallel tool calls.","Handle tool calls non-streaming (use call() instead of stream()) where multi-tool-call responses are supported."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"long parallel = chunk.getChoices().stream()\n    .flatMap(c -> c.getMessage().getToolCalls() == null ? Stream.empty() : c.getMessage().getToolCalls().stream())\n    .count();\nif (parallel > 1) { /* switch to non-streaming path */ }","typeGuard":null,"tryCatchPattern":"try {\n    return chatModel.stream(prompt);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"only one tool call\")) { return chatModel.call(prompt); } // non-streaming fallback\n    throw e;\n}","preventionTips":["Use the non-streaming call() path when prompts enable multiple tools that may be invoked in parallel.","Upgrade to a Spring AI version with parallel tool-call streaming support.","Instruct the model via system prompt to issue one tool call at a time when streaming."],"tags":["streaming","tool-calling","unsupported-operation","mistral"],"backgroundTag":"unsupported-operation","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"}