CoplayDev/unity-mcp · error · ValueError

batch_execute supports up to

Error message

batch_execute supports up to {max_commands} commands (configured in Unity); received {len(commands)}

What it means

batch_execute enforces a configurable upper bound on the number of sub-commands per batch. The limit is read from Unity's editor state (settings.batch_execute_max_commands) and cached module-level; if unavailable it falls back to DEFAULT_MAX_COMMANDS_PER_BATCH (25). The hard ceiling is ABSOLUTE_MAX_COMMANDS_PER_BATCH (100). This error fires when len(commands) exceeds the resolved limit, preventing an AI from submitting an unbounded batch that could overwhelm Unity's main thread.

Solutions

  1. Split the batch into chunks of at most max_commands (default 25) and call batch_execute multiple times.
  2. Raise the limit in Unity's MCP for Unity settings (batch_execute_max_commands), up to the hard ceiling of 100.
  3. Call invalidate_cached_max_commands() if you changed the Unity setting at runtime and the old value is cached.
  4. Read the current limit from the editor_state resource before building the batch to size it correctly.

Example fix

# before
await batch_execute(ctx, commands=[...50 items...])
# after — chunk into batches of <= 25
for i in range(0, len(commands), 25):
    await batch_execute(ctx, commands=commands[i:i+25])
Defensive patterns

Strategy: validation

Validate before calling

# Read the current limit and chunk accordingly
from services.tools.batch_execute import _get_max_commands_from_editor_state, DEFAULT_MAX_COMMANDS_PER_BATCH

max_cmds = DEFAULT_MAX_COMMANDS_PER_BATCH  # safe fallback
# In async context: max_cmds = await _get_max_commands_from_editor_state(ctx)
chunk_size = min(max_cmds, len(commands)) if commands else 0
chunks = [commands[i:i + max_cmds] for i in range(0, len(commands), max_cmds)]

Prevention

When it happens

Trigger: A caller submits 30+ commands when the configured limit is 25 (default); an AI generates a very large batch (e.g., 50 object creations) in one call; the Unity-side setting was lowered from the default; the cached limit is stale after a config change (the cache is only invalidated by invalidate_cached_max_commands()).

Common situations: An LLM over-generates commands in a single batch; a script programmatically builds a large batch; the Unity project lowered batch_execute_max_commands in settings; the module cache holds an old value after the setting changed (cache is not auto-cleared).

Related errors


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

Appendix: source

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

    ctx: Context,
    commands: Annotated[list[dict[str, Any]], "List of commands with 'tool' and 'params' keys."],
    parallel: Annotated[bool | None,
                        "Attempt to run read-only commands in parallel"] = None,
    fail_fast: Annotated[bool | None,
                         "Stop processing after the first failure"] = None,
    max_parallelism: Annotated[int | None,
                               "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 = {}

View on GitHub (pinned to c21bf496bc)