mastra-ai/mastra · error · ApiCliError

INVALID_JSON

INVALID_JSON

Error message

INVALID_JSON: Input JSON must be an object

What it means

The Mastra API CLI parses the inline `--input` JSON argument with JSON.parse and then requires the result to be a plain object. When the parsed value is null, a scalar (string/number/boolean), or an array, parseInput throws INVALID_JSON with 'Input JSON must be an object', because API command inputs are always Record<string, unknown> bodies merged with path params and query values.

Source

Thrown at packages/cli/src/commands/api/input.ts:21

export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };

export function parseInput(descriptor: ApiCommandDescriptor, input?: string): Record<string, unknown> | undefined {
  if (!descriptor.acceptsInput) return undefined;

  if (!input) {
    if (descriptor.inputRequired) {
      throw new ApiCliError('MISSING_INPUT', 'Command requires a single inline JSON input argument', {
        command: `mastra api ${descriptor.name}`,
      });
    }
    return undefined;
  }

  try {
    const parsed = JSON.parse(input);
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
      throw new ApiCliError('INVALID_JSON', 'Input JSON must be an object');
    }
    return parsed as Record<string, unknown>;
  } catch (error) {
    if (error instanceof ApiCliError) throw error;
    throw new ApiCliError('INVALID_JSON', 'Input must be valid JSON', {
      message: error instanceof Error ? error.message : String(error),
    });
  }
}

export function resolvePathParams(
  descriptor: ApiCommandDescriptor,
  positionalValues: string[],
  input?: Record<string, unknown>,
): Record<string, string> {
  const params: Record<string, string> = {};

  descriptor.positionals.forEach((name, index) => {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Wrap the payload in an object, e.g. --input '{"items":[1,2,3]}' instead of --input '[1,2,3]'
  2. Validate with `node -e 'const v=JSON.parse(process.argv[1]); if(!v||Array.isArray(v)||typeof v!=="object") throw 0' -- <your-arg>` before invoking
  3. Check the command's schema via the CLI (e.g. a schema/describe subcommand) to see the expected top-level shape
  4. If the endpoint truly accepts an array body, file/inspect a CLI issue — the CLI currently only accepts object inputs

Example fix

// before
mastra api agents generate --input '"Hello"'
// after
mastra api agents generate --input '{"messages":"Hello"}'
Defensive patterns

Strategy: validation

Validate before calling

function isValidObjectInput(raw) { try { const v = JSON.parse(raw); return v !== null && typeof v === 'object' && !Array.isArray(v); } catch { return false; } }
if (!isValidObjectInput(myInput)) { /* fix payload before invoking CLI */ }

Type guard

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

Try / catch

try { await exec('mastra', ['api', cmd, '--input', raw]); } catch (e) { if (String(e).includes('INVALID_JSON')) { console.error('Input must be a JSON object, e.g. {"key": "value"}'); } else throw e; }

Prevention

When it happens

Trigger: Running `mastra api <command> --input 'null'`, `--input '"text"'`, `--input '5'`, or `--input '[1,2,3]'` — any value that parses as JSON but is not a non-null, non-array object.

Common situations: Users passing a bare JSON array as a body because the endpoint conceptually accepts a list; shell quoting stripping quotes so `--input {}` becomes a literal `{}` string vs `--input '"x"'`; copy-pasting an array payload from a REST example; forgetting that the CLI expects a top-level object keyed by field name.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/3f8abdab31acba5c. Report an issue: GitHub.