ComposioHQ/composio · error · InvalidParams
Tool arguments must resolve to an object, received {type(val
Error message
Tool arguments must resolve to an object, received {type(value).__name__} What it means
After normalize_tool_arguments (JSON-parsed or passed through), the value is not a dict — _as_dict only accepts mappings and raises InvalidParams naming the actual type (e.g. list, str, int, NoneType). Tool argument payloads must be a JSON object at the top level.
Source
Thrown at python/composio/utils/shared.py:104
if isinstance(arguments, str):
stripped = arguments.strip()
if not stripped:
return {}
try:
parsed = json.loads(stripped)
except json.JSONDecodeError as e:
raise InvalidParams(
f"Tool arguments were provided as a string that is not valid JSON: {e}"
) from e
return _as_dict(parsed)
return _as_dict(arguments)
def _as_dict(value: t.Any) -> t.Dict[str, t.Any]:
if isinstance(value, dict):
return value
raise InvalidParams(
f"Tool arguments must resolve to an object, received {type(value).__name__}"
)
def validate_and_serialize_tool_arguments(
args_schema: t.Type[BaseModel],
arguments: t.Dict[str, t.Any],
) -> t.Dict[str, t.Any]:
"""Validate provider arguments and serialize only values the backend should see.
Pydantic models use ``None`` for optional fields that were not supplied, so a
plain ``model_dump()`` changes omission into an explicit null. Conversely,
``exclude_unset=True`` also drops defaults declared by the source JSON Schema.
Generated models track fields with declared defaults. Combining that metadata
recursively with each selected model's ``model_fields_set`` preserves aliases,
explicit nulls, defaults, and validated dynamic extras without replaying the
source schema during execution.
"""View on GitHub (pinned to 64b1b85502)
Solutions
- Ensure the top-level arguments value is an object/dict — wrap scalars: use {"value": x} if the schema expects a field, or {} for no args
- Check the tool's input schema: if it legitimately takes an array, the SDK still needs {"items": [...]}-style wrapping per the schema
- Default None to {} before calling
- If an LLM produced it, tighten the prompt/tool schema so it emits a single JSON object
Example fix
# before
tool.run(arguments=[{"query": "a"}, {"query": "b"}])
# after
tool.run(arguments={"queries": [{"query": "a"}, {"query": "b"}]}) Defensive patterns
Strategy: type-guard
Validate before calling
def ensure_args_object(args):
if args is None: return {}
if isinstance(args, str):
import json
args = json.loads(args) if args.strip() else {}
return args Type guard
def is_args_object(args) -> bool:
if isinstance(args, str):
import json
try: args = json.loads(args)
except json.JSONDecodeError: return False
return isinstance(args, dict) Try / catch
from composio.exceptions import InvalidParams
try:
tool.run(args)
except InvalidParams as e:
if "must resolve to an object" in str(e):
tool.run({"value": args}) # wrap per schema Prevention
- Always pass a dict (or JSON object string) as arguments
- Default None/missing args to {}
- Wrap scalar payloads in a named field matching the tool schema
When it happens
Trigger: arguments=[1,2,3] (a JSON array), arguments='"just a string"' (valid JSON but a string scalar), arguments=42, or arguments=None slipping through. Also a JSON string containing an array like '[{"a":1}]'.
Common situations: LLMs wrapping args in a list; models returning a bare string; code forwarding an unwrapped variable (passing the value of a 'arguments' key that itself is a scalar); optional-args paths where None isn't defaulted to {}.
Related errors
- Tool arguments were provided as a string that is not valid J
- Pass either `sandbox` or `workbench`, not both. `workbench`
- {error.message}
- Unrecognized key(s) in object: {', '.join(repr(key) for key
- Tool arguments exceed maximum nesting depth of {MAX_NODE_DEP
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/394e5f159797f2f8.
Report an issue: GitHub.