From 70ebc0ab501b912800a323e134388ccc1d085bd4 Mon Sep 17 00:00:00 2001 From: Nam Thanh Nguyen Macbook Date: Wed, 5 Aug 2026 22:29:14 +0700 Subject: [PATCH] fix: sanitize malformed tool call arguments to prevent vLLM 400 errors When LLMs generate malformed JSON in tool_calls.function.arguments (e.g., missing commas, truncated due to max_tokens), Continue stores this in conversation history and sends it back to the server on subsequent turns. vLLM validates JSON in tool_calls arguments during request preprocessing and rejects malformed input with a 400 error. Fix: validate JSON before sending tool_calls arguments back to server. If invalid, wrap in a valid JSON envelope ({ _raw: original }) to prevent server rejection while preserving the original content. Root causes of malformed JSON: 1. max_tokens truncation cutting off mid-JSON 2. Reasoning token leak into arguments field 3. Model hallucination producing invalid syntax 4. Special characters not properly escaped Related: https://github.com/vllm-project/vllm/issues/43995 --- core/llm/openaiTypeConverters.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/core/llm/openaiTypeConverters.ts b/core/llm/openaiTypeConverters.ts index fb4673e11be..cd8987db10c 100644 --- a/core/llm/openaiTypeConverters.ts +++ b/core/llm/openaiTypeConverters.ts @@ -155,14 +155,25 @@ export function toChatMessage( // Add tool calls if present if (message.toolCalls) { - msg.tool_calls = message.toolCalls.map((toolCall) => ({ - id: toolCall.id!, - type: toolCall.type!, - function: { - name: toolCall.function?.name!, - arguments: toolCall.function?.arguments || "{}", - }, - })); + msg.tool_calls = message.toolCalls.map((toolCall) => { + // Sanitize arguments — ensure valid JSON to prevent vLLM 400 errors + // when malformed tool arguments from previous turns are sent back in history + let args = toolCall.function?.arguments || "{}"; + try { + JSON.parse(args); + } catch { + // If arguments are not valid JSON, wrap them to prevent server rejection + args = JSON.stringify({ _raw: args }); + } + return { + id: toolCall.id!, + type: toolCall.type!, + function: { + name: toolCall.function?.name!, + arguments: args, + }, + }; + }); } // Preserving reasoning blocks