ComposioHQ/composio · error · ComposioInvalidToolArgumentsError
${describeTool(toolSlug)} received arguments as a string tha
Error message
${describeTool(toolSlug)} received arguments as a string that is not valid JSON What it means
Thrown by normalizeToolArguments when the model (or caller) passes tool arguments as a string that fails JSON.parse. Composio tolerates models that emit stringified JSON (issue #2406), but the string must still be syntactically valid JSON; a parse failure raises ComposioInvalidToolArgumentsError with the original SyntaxError as cause.
Source
Thrown at ts/packages/core/src/utils/toolArguments.ts:49
* @param toolSlug - Optional tool slug, used to enrich the error message.
* @returns The normalized arguments as a `Record<string, unknown>`.
*/
export function normalizeToolArguments(input: unknown, toolSlug?: string): Record<string, unknown> {
if (input === null || input === undefined) {
return {};
}
if (typeof input === 'string') {
const trimmed = input.trim();
if (trimmed === '') {
return {};
}
let parsed: unknown;
try {
parsed = JSON.parse(trimmed);
} catch (cause) {
throw new ComposioInvalidToolArgumentsError(
`${describeTool(toolSlug)} received arguments as a string that is not valid JSON`,
{ cause: cause instanceof Error ? cause : undefined }
);
}
return assertPlainObject(parsed, toolSlug);
}
return assertPlainObject(input, toolSlug);
}
function assertPlainObject(value: unknown, toolSlug?: string): Record<string, unknown> {
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return value as Record<string, unknown>;
}
const actual = Array.isArray(value) ? 'array' : typeof value;
throw new ComposioInvalidToolArgumentsError(
`${describeTool(toolSlug)} expected arguments to be an object, received ${actual}`View on GitHub (pinned to 64b1b85502)
Solutions
- Inspect the raw arguments string before calling the tool and log it when JSON.parse fails
- If the model output is reliably non-JSON, repair it upstream (e.g. jsonrepair, or instruct the model with stricter tool-call formatting)
- Catch ComposioInvalidToolArgumentsError and re-prompt the model with the parse error so it re-emits valid JSON
- Ensure you pass an object, not JSON.stringify-ed arguments, when you already control the call
Example fix
// before
await executeTool('foo', "{'path': './a'}");
// after
await executeTool('foo', { path: './a' }); Defensive patterns
Strategy: validation
Validate before calling
const args = typeof raw === 'string' ? raw : JSON.stringify(raw ?? '');
let parsed: unknown;
try { parsed = JSON.parse(args); } catch { /* repair or reject before calling executeTool */ } Type guard
function isJsonObject(s: string): boolean { try { const v = JSON.parse(s); return typeof v === 'object' && v !== null && !Array.isArray(v); } catch { return false; } } Try / catch
try { await executeTool(slug, args); } catch (e) { if (e instanceof ComposioInvalidToolArgumentsError) { /* re-prompt model with e.message */ } throw e; } Prevention
- Always pass plain objects to executeTool when you control the call
- Log the raw argument string on failure to spot model formatting bugs
- Use structured outputs / strict tool-call mode in your model provider
When it happens
Trigger: Calling executeTool (directly or via provider wrapTool/executeTool paths) with args[0] as a string like "{'foo': 1}" (single quotes), truncated JSON, or text with a leading BOM/whitespace-only content prefixed non-JSON.
Common situations: LLM emits malformed JSON tool calls (unquoted keys, trailing commas, single quotes); proxy or agent framework concatenates arguments; string built by template literal with unescaped quotes.
Related errors
- ${describeTool(toolSlug)} expected arguments to be an object
- {error.message}
- Unrecognized key(s) in object: {', '.join(repr(key) for key
- Tool arguments were provided as a string that is not valid J
- experimental_subAgent() target must be "claude", "codex", or
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/d680f1fdb2343891.
Report an issue: GitHub.