CoplayDev/unity-mcp · error · ValueError

Command '{tool_name}' must specify parameters as an object/d

Error message

Command '{tool_name}' must specify parameters as an object/dict

What it means

In batch_execute, each command's 'params' value must be either absent (defaults to {}), None (normalized to {}), or a dict. The check `not isinstance(params, dict)` after the None-normalization rejects lists, strings, numbers, and other non-mapping types. params carries the keyword arguments forwarded to the target Unity tool, so it must be a JSON object. The tool_name is included in the message for context.

Source

Thrown at Server/src/services/tools/batch_execute.py:116

        )

    normalized_commands: list[dict[str, Any]] = []
    for index, command in enumerate(commands):
        if not isinstance(command, dict):
            raise ValueError(
                f"Command at index {index} must be an object with 'tool' and 'params' keys")

        tool_name = command.get("tool")
        params = command.get("params", {})

        if not tool_name or not isinstance(tool_name, str):
            raise ValueError(
                f"Command at index {index} is missing a valid 'tool' name")

        if params is None:
            params = {}
        if not isinstance(params, dict):
            raise ValueError(
                f"Command '{tool_name}' must specify parameters as an object/dict")

        if "unity_instance" in params:
            raise ValueError(
                f"Command '{tool_name}' at index {index} contains 'unity_instance'. "
                "Per-command instance routing is not supported inside batch_execute. "
                "Set unity_instance on the outer batch_execute call to route the entire batch."
            )

        normalized_commands.append({
            "tool": tool_name,
            "params": params,
        })

    payload: dict[str, Any] = {
        "commands": normalized_commands,
    }

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Pass params as a JSON object (dict) of keyword arguments: {"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube"}}.
  2. If the tool takes no parameters, omit params or set it to {}.
  3. Check the tool_name in the message to locate which command has the bad params.

Example fix

// before
commands=[{"tool": "manage_gameobject", "params": ["create", "Cube"]}]
// after
commands=[{"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube"}}]
Defensive patterns

Strategy: type-guard

Validate before calling

# Ensure params is a dict (or absent/None) for each command
for i, c in enumerate(commands):
    params = c.get("params", {})
    if params is None:
        c["params"] = {}
    elif not isinstance(params, dict):
        raise ValueError(f"Command {i} ({c.get('tool')}) params must be a dict, got {type(params).__name__}")

Type guard

def all_params_are_dicts(commands: list) -> bool:
    for c in commands:
        if not isinstance(c, dict):
            continue
        p = c.get("params", {})
        if p is not None and not isinstance(p, dict):
            return False
    return True

Prevention

When it happens

Trigger: A command passes params as a list: {"tool": "x", "params": [1, 2]}; params is a JSON string; params is a number; a caller serializes params as a query string instead of an object.

Common situations: An LLM generates params as an array of positional args instead of a kwargs object; a caller confuses params with a list of values; a serialization bug where params is double-encoded as a string.

Related errors


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/48df2e532743d942. Report an issue: GitHub.