CoplayDev/unity-mcp · error · TypeError
Command parameters must be a dict for HTTP transport
Error message
Command parameters must be a dict for HTTP transport
What it means
Raised by send_with_unity_instance in HTTP mode when the resolved params value is not a dict. The HTTP request body is built from a JSON object, so a non-dict (list, str, None after coercion, custom object) cannot be serialized into the expected command payload shape.
Source
Thrown at Server/src/transport/unity_transport.py:57
return None
async def send_with_unity_instance(
send_fn: Callable[..., Awaitable[T]],
unity_instance: str | None,
*args,
user_id: str | None = None,
**kwargs,
) -> T:
if _is_http_transport():
if not args:
raise ValueError("HTTP transport requires command arguments")
command_type = args[0]
params = args[1] if len(args) > 1 else kwargs.get("params")
if params is None:
params = {}
if not isinstance(params, dict):
raise TypeError(
"Command parameters must be a dict for HTTP transport")
# Auto-resolve user_id from HTTP request API key (remote-hosted mode)
if user_id is None:
user_id = await _resolve_user_id_from_request()
# Auth check
if config.http_remote_hosted and not user_id:
return normalize_unity_response(
MCPResponse(
success=False,
error="auth_required",
message="API key required",
).model_dump()
)
retry_on_reload = kwargs.pop("retry_on_reload", True)
if not isinstance(retry_on_reload, bool):View on GitHub (pinned to c21bf496bc)
Solutions
- Pass params as a plain dict (use model_dump() for Pydantic models, or json.loads then re-wrap).
- Validate isinstance(params, dict) at the wrapper boundary before calling send_with_unity_instance.
- Normalize at the call site: params = dict(params) when params is a mapping-like object.
Example fix
// before await send_with_unity_instance(send_fn, instance, 'manage_gameobject', some_model) // after await send_with_unity_instance(send_fn, instance, 'manage_gameobject', some_model.model_dump())
Defensive patterns
Strategy: type-guard
Validate before calling
if not isinstance(params, dict):
params = params.model_dump() if hasattr(params, 'model_dump') else dict(params)
if not isinstance(params, dict):
raise TypeError('params must be a dict') Type guard
def is_command_params(v) -> bool:
return isinstance(v, dict) Try / catch
try:
await send_with_unity_instance(send_fn, instance, cmd, params)
except TypeError as e:
if 'must be a dict' in str(e):
await send_with_unity_instance(send_fn, instance, cmd, dict(params)) Prevention
- Coerce Pydantic models with model_dump() before sending
- Never pass raw JSON strings or lists as params
- Assert isinstance(params, dict) at the wrapper boundary
When it happens
Trigger: args[1] (or kwargs['params']) is present but isinstance(params, dict) is false. The guard at unity_transport.py:55-57 fires.
Common situations: A tool wrapper passed a Pydantic model or a JSON string instead of a plain dict; params came from json.loads of an array; a refactor changed the params type without adapting the call site.
Related errors
- HTTP transport requires command arguments
- Port-based targeting ('{value}') is not supported in HTTP tr
- fal {phase} failed (status={res?.Status}): {detail}
- fal {phase} failed (status={res?.Status}): {detail}
- {context}: refusing to send credentials to an unexpected hos
AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13).
Data as JSON: /api/errors/c35ee55e5a593ff4.
Report an issue: GitHub.