ComposioHQ/composio · error · InvalidParams
Tool arguments were provided as a string that is not valid J
Error message
Tool arguments were provided as a string that is not valid JSON: {e} What it means
normalize_tool_arguments accepts tool arguments as a JSON string (as produced by many LLM providers), but json.loads failed on the non-empty string. The InvalidParams error wraps the json.JSONDecodeError so you can see the exact parse position.
Source
Thrown at python/composio/utils/shared.py:93
- A string is JSON-parsed; an empty / whitespace-only string becomes ``{}``.
- Anything that does not resolve to a dict (lists, primitives, unparseable
strings, JSON that parses to a non-object) raises :class:`InvalidParams`.
:param arguments: Raw arguments as received from the model / framework.
:return: The normalized arguments as a dict.
:raises InvalidParams: If the arguments cannot be resolved to a dict.
"""
if arguments is None:
return {}
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],View on GitHub (pinned to 64b1b85502)
Solutions
- Pass a dict when you have structured data: normalize_tool_arguments({...}) instead of a string
- If you must handle strings, pre-validate with json.loads and repair common issues (single quotes, fences, truncation) before calling
- Enable provider-side JSON/structured output so the model emits valid JSON
- Log the raw string on failure — the wrapped JSONDecodeError position pinpoints the breakage
Example fix
# before
result = tool.run(arguments="{'query': 'x'}")
# after
result = tool.run(arguments='{"query": "x"}')
# or better
result = tool.run(arguments={"query": "x"}) Defensive patterns
Strategy: try-catch
Validate before calling
import json
def parse_args_string(s):
if not isinstance(s, str): return s
s = s.strip()
if not s: return {}
try: return json.loads(s)
except json.JSONDecodeError: return None # caller repairs or rejects Type guard
def is_json_object_string(s: str) -> bool:
try:
return isinstance(json.loads(s), dict)
except (json.JSONDecodeError, TypeError):
return False Try / catch
from composio.exceptions import InvalidParams
try:
args = normalize_tool_arguments(raw)
except InvalidParams as e:
raw = repair_json_like(raw) # fix quotes/fences, or ask model to retry
args = normalize_tool_arguments(raw) Prevention
- Pass dicts, not strings, whenever you have structured data
- Enable provider structured-output/JSON mode for tool calls
- Strip markdown fences and whitespace from LLM output before parsing
When it happens
Trigger: Passing arguments='{'query': 'x'}' (single quotes, invalid JSON), a truncated response, a string with a leading/trailing code fence or prose, or a Python-repr dict ("{'a': 1}") straight from an LLM or str(dict).
Common situations: Models emitting Python-dict syntax instead of strict JSON; tool-call arguments streamed and truncated; markdown fences around JSON; double-encoded JSON ('\"{\\"a\\":1}\"') from a provider quirk; ad-hoc scripts passing str(payload).
Related errors
- Tool arguments must resolve to an object, received {type(val
- experimental_subAgent() expected valid JSON output for struc
- ${describeTool(toolSlug)} received arguments as a string tha
- Pass either `sandbox` or `workbench`, not both. `workbench`
- {error.message}
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/be71cae1c44bf3a1.
Report an issue: GitHub.