hiyouga/LlamaFactory · error · HTTPException

Invalid tools

Error message

Invalid tools

What it means

Raised as HTTP 400 when the request includes a non-empty tools array but serializing each tool's .function via dictify() raises json.JSONDecodeError. In practice this means a tool definition is malformed enough that its function object cannot be dictified and dumped — e.g. custom/non-FunctionTool objects or arguments already containing broken JSON.

Source

Thrown at src/llamafactory/api/chat.py:174

                        check_ssrf_url(audio_url)
                        audio_stream = requests.get(audio_url, stream=True).raw

                    audios.append(audio_stream)
                else:
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST, detail=f"Invalid input type {input_item.type}."
                    )

            input_messages.append({"role": ROLE_MAPPING[message.role], "content": text_content})
        else:
            input_messages.append({"role": ROLE_MAPPING[message.role], "content": message.content})

    tool_list = request.tools
    if isinstance(tool_list, list) and len(tool_list):
        try:
            tools = json.dumps([dictify(tool.function) for tool in tool_list], ensure_ascii=False)
        except json.JSONDecodeError:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid tools")
    else:
        tools = None

    return input_messages, system, tools, images or None, videos or None, audios or None


def _create_stream_chat_completion_chunk(
    completion_id: str,
    model: str,
    delta: "ChatCompletionMessage",
    index: Optional[int] = 0,
    finish_reason: Optional["Finish"] = None,
) -> str:
    choice_data = ChatCompletionStreamResponseChoice(index=index, delta=delta, finish_reason=finish_reason)
    chunk = ChatCompletionStreamResponse(id=completion_id, model=model, choices=[choice_data])
    return jsonify(chunk)

View on GitHub (pinned to f28afaf635)

Solutions

  1. Ensure each tool is {type: 'function', function: {name, description?, parameters (JSON schema)}}.
  2. Validate each tool with json.dumps(tool.function) client-side before sending; if it throws, fix the object.
  3. Do not confuse tools (definitions) with messages containing role 'tool' (results) — only send definitions in tools.
  4. Simplify to a single minimal tool and re-add tools one by one to find the malformed entry.

Example fix

// before
tools: [{ name: 'get_weather', parameters: {type:'object'} }]  // missing function wrapper
// after
tools: [{
  type: 'function',
  function: { name: 'get_weather', description: 'Get weather', parameters: {type:'object', properties:{city:{type:'string'}}, required:['city']} }
}]
Defensive patterns

Strategy: validation

Validate before calling

import json
def tools_serializable(tools):
    if not tools:
        return True
    try:
        for t in tools:
            json.dumps(t.get("function", t), default=str)
        return True
    except (TypeError, ValueError, KeyError):
        return False

Type guard

const isToolDef = (t) => t?.type === 'function' && typeof t.function?.name === 'string';
const allToolsValid = (tools) => !tools || tools.every(isToolDef);

Try / catch

try { ... } catch (e) { if (e.status === 400 && e.detail === 'Invalid tools') { tools = tools.filter(isToolDef); /* resend without the malformed tool */ } }

Prevention

When it happens

Trigger: POST /v1/chat/completions with tools: [...] where a tool entry is not a proper {type:'function', function:{name, description, parameters}} object; or where function.arguments is a string containing invalid JSON that the model/round-trip previously mangled.

Common situations: Sending back assistant tool_calls payloads as tools; using a client SDK that emits a different tool schema; hand-written tool JSON with missing 'function' wrapper; proxying another provider's tool format verbatim.

Related errors


AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14). Data as JSON: /api/errors/bf74448c40a5c772. Report an issue: GitHub.