{"record":{"id":"394e5f159797f2f8","repo":"ComposioHQ/composio","slug":"tool-arguments-must-resolve-to-an-object-received","errorCode":null,"errorMessage":"Tool arguments must resolve to an object, received {type(value).__name__}","messagePattern":"Tool arguments must resolve to an object, received (.+?)","errorType":"validation","errorClass":"InvalidParams","httpStatus":null,"severity":"error","filePath":"python/composio/utils/shared.py","lineNumber":104,"sourceCode":"    if isinstance(arguments, str):\n        stripped = arguments.strip()\n        if not stripped:\n            return {}\n        try:\n            parsed = json.loads(stripped)\n        except json.JSONDecodeError as e:\n            raise InvalidParams(\n                f\"Tool arguments were provided as a string that is not valid JSON: {e}\"\n            ) from e\n        return _as_dict(parsed)\n\n    return _as_dict(arguments)\n\n\ndef _as_dict(value: t.Any) -> t.Dict[str, t.Any]:\n    if isinstance(value, dict):\n        return value\n    raise InvalidParams(\n        f\"Tool arguments must resolve to an object, received {type(value).__name__}\"\n    )\n\n\ndef validate_and_serialize_tool_arguments(\n    args_schema: t.Type[BaseModel],\n    arguments: t.Dict[str, t.Any],\n) -> t.Dict[str, t.Any]:\n    \"\"\"Validate provider arguments and serialize only values the backend should see.\n\n    Pydantic models use ``None`` for optional fields that were not supplied, so a\n    plain ``model_dump()`` changes omission into an explicit null. Conversely,\n    ``exclude_unset=True`` also drops defaults declared by the source JSON Schema.\n    Generated models track fields with declared defaults. Combining that metadata\n    recursively with each selected model's ``model_fields_set`` preserves aliases,\n    explicit nulls, defaults, and validated dynamic extras without replaying the\n    source schema during execution.\n    \"\"\"","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/shared.py#L86-L122","documentation":"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.","triggerScenarios":"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}]'.","commonSituations":"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 {}.","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"],"exampleFix":"# before\ntool.run(arguments=[{\"query\": \"a\"}, {\"query\": \"b\"}])\n# after\ntool.run(arguments={\"queries\": [{\"query\": \"a\"}, {\"query\": \"b\"}]})","handlingStrategy":"type-guard","validationCode":"def ensure_args_object(args):\n    if args is None: return {}\n    if isinstance(args, str):\n        import json\n        args = json.loads(args) if args.strip() else {}\n    return args","typeGuard":"def is_args_object(args) -> bool:\n    if isinstance(args, str):\n        import json\n        try: args = json.loads(args)\n        except json.JSONDecodeError: return False\n    return isinstance(args, dict)","tryCatchPattern":"from composio.exceptions import InvalidParams\ntry:\n    tool.run(args)\nexcept InvalidParams as e:\n    if \"must resolve to an object\" in str(e):\n        tool.run({\"value\": args})  # wrap per schema","preventionTips":["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"],"tags":["tool-arguments","type-mismatch","invalid-params"],"backgroundTag":"invalid-tool-arguments-type","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}