CoplayDev/unity-mcp · error · ValueError

Command at index {index} must be an object with 'tool' and '

Error message

Command at index {index} must be an object with 'tool' and 'params' keys

What it means

During per-command normalization in batch_execute, each element of the commands list must be a dict (JSON object). The check `not isinstance(command, dict)` rejects lists, strings, numbers, None, or any non-mapping type. The error message includes the offending index so the caller can locate the malformed entry. This is a structural contract violation: each command must have 'tool' and 'params' keys.

Source

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

                               "Hint for the maximum number of parallel workers"] = None,
) -> dict[str, Any]:
    """Proxy the batch_execute tool to the Unity Editor transporter."""
    unity_instance = await get_unity_instance_from_context(ctx)

    if not isinstance(commands, list) or not commands:
        raise ValueError(
            "'commands' must be a non-empty list of command specifications")

    max_commands = await _get_max_commands_from_editor_state(ctx)
    if len(commands) > max_commands:
        raise ValueError(
            f"batch_execute supports up to {max_commands} commands (configured in Unity); received {len(commands)}"
        )

    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'. "

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Wrap each command as an object: {"tool": "<name>", "params": {...}}.
  2. Check the error message for the index, then fix that specific entry in the commands list.
  3. Validate the list shape before calling: all(isinstance(c, dict) for c in commands).

Example fix

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

Strategy: validation

Validate before calling

# Ensure every command is a dict before calling batch_execute
if not all(isinstance(c, dict) for c in commands):
    bad = [i for i, c in enumerate(commands) if not isinstance(c, dict)]
    raise ValueError(f"Commands at indices {bad} are not objects")

Type guard

def all_commands_are_dicts(commands: list) -> bool:
    return isinstance(commands, list) and all(isinstance(c, dict) for c in commands)

Prevention

When it happens

Trigger: A caller passes commands=["manage_gameobject", {...}] mixing a bare string with objects; an LLM emits commands=["create_cube"] treating batch_execute like a variadic string list; a JSON array where one element is a string or number instead of an object; a None element from a sparse array.

Common situations: An AI confuses batch_execute's command shape with a simpler API; a programmatic builder appends a tool name string instead of a {tool, params} dict; JSON deserialization of a mixed-type array; a copy-paste error mixing formats.

Related errors


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