ComposioHQ/composio · error · ComposioInvalidToolArgumentsError

${describeTool(toolSlug)} expected arguments to be an object

Error message

${describeTool(toolSlug)} expected arguments to be an object, received ${actual}

What it means

Thrown by assertPlainObject inside normalizeToolArguments when tool arguments are neither a plain object nor a JSON string that parses to one. After normalization the SDK requires a Record<string, unknown>; arrays, primitives, or null are rejected with the actual received type in the message.

Source

Thrown at ts/packages/core/src/utils/toolArguments.ts:66

    } 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}`
  );
}

function describeTool(toolSlug?: string): string {
  return toolSlug ? `Tool '${toolSlug}'` : 'Tool';
}

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Pass a plain object matching the tool's inputParameters schema
  2. If the caller supplies a JSON string, ensure it parses to an object (not array/scalar) and is not double-stringified
  3. Catch ComposioInvalidToolArgumentsError and map it to a model-facing retry with the expected schema

Example fix

// before
await executeTool('foo', 'not json');
// after
await executeTool('foo', { query: 'not json' });
Defensive patterns

Strategy: type-guard

Validate before calling

function normalize(args: unknown): Record<string, unknown> | null { if (typeof args === 'string') { try { args = JSON.parse(args); } catch { return null; } } return (typeof args === 'object' && args !== null && !Array.isArray(args)) ? args as Record<string, unknown> : null; }

Type guard

const isPlainObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);

Try / catch

catch (e) { if (e instanceof ComposioInvalidToolArgumentsError) return { error: e.message }; throw e; }

Prevention

When it happens

Trigger: Calling executeTool with args[0] = [], 42, "literal text", null, or a JSON string like '"hi"' / '[1,2]' that parses to a non-object; also double-encoded strings like '"{\"a\":1}"'.

Common situations: Model emits a bare string or array as tool input; agent framework forwards positional args; tool schema expects object but caller passes a list of positional values.

Related errors


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