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

  1. Inspect the raw arguments string before calling the tool and log it when JSON.parse fails
  2. If the model output is reliably non-JSON, repair it upstream (e.g. jsonrepair, or instruct the model with stricter tool-call formatting)
  3. Catch ComposioInvalidToolArgumentsError and re-prompt the model with the parse error so it re-emits valid JSON
  4. 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

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


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/d680f1fdb2343891. Report an issue: GitHub.