mastra-ai/mastra · info · HTTPException

Messages array is required

Error message

Messages array is required

What it means

POST /processors/:processorId/execute throws this HTTP 400 when the `messages` body field is missing or is not an array. The handler feeds `messages` into a MessageList before invoking the processor, so it must be an array of message objects.

Source

Thrown at packages/server/src/server/handlers/processors.ts:217

  responseSchema: executeProcessorResponseSchema,
  summary: 'Execute processor',
  description: 'Executes a specific processor with the provided input data',
  tags: ['Processors'],
  requiresAuth: true,
  handler: async ({ mastra, processorId, ...bodyParams }) => {
    try {
      const { phase, messages } = bodyParams;

      if (!processorId) {
        throw new HTTPException(400, { message: 'Processor ID is required' });
      }

      if (!phase) {
        throw new HTTPException(400, { message: 'Phase is required' });
      }

      if (!messages || !Array.isArray(messages)) {
        throw new HTTPException(400, { message: 'Messages array is required' });
      }

      // Get the processor from Mastra's registered processors
      let processor;
      try {
        processor = mastra.getProcessorById(processorId);
      } catch {
        // getProcessorById throws if not found, try by key
        const processors = mastra.listProcessors() || {};
        processor = processors[processorId as keyof typeof processors];
      }

      if (!processor) {
        throw new HTTPException(404, { message: 'Processor not found' });
      }

      const messageList = new MessageList();
      messageList.add(messages as unknown as MessageInput[], 'input');

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Wrap messages in an array: `"messages": [{ role: 'user', content: '...' }]`.
  2. Ensure each entry is a valid message shape (role + content) accepted by MessageList.
  3. Default to an empty array client-side when there is no history, if the endpoint contract allows it.

Example fix

// before
body: JSON.stringify({ phase: 'input', messages: { role: 'user', content: 'hi' } })
// after
body: JSON.stringify({ phase: 'input', messages: [{ role: 'user', content: 'hi' }] })
Defensive patterns

Strategy: validation

Validate before calling

if (!Array.isArray(messages)) {
  throw new Error('messages must be an array of message objects before calling processor execute');
}
for (const m of messages) {
  if (typeof m !== 'object' || m === null || !('role' in m)) throw new Error('invalid message shape');
}

Type guard

function isMessageArray(v: unknown): v is Array<{ role: string; content: unknown }> {
  return Array.isArray(v) && v.every(m => typeof m === 'object' && m !== null && 'role' in m);
}

Try / catch

try {
  const res = await fetch(url, { method: 'POST', body: JSON.stringify({ phase, messages }) });
  if (res.status === 400 && (await res.json()).message === 'Messages array is required') {
    throw new Error('Client bug: messages must be an array');
  }
  return await res.json();
} catch (e) {
  console.error(e);
  throw e;
}

Prevention

When it happens

Trigger: POST /processors/:id/execute with `{ phase: 'input' }` only, or with `messages` set to a single object/string/null instead of an array — the guard is `if (!messages || !Array.isArray(messages))`.

Common situations: Sending one message object directly instead of wrapping it in an array; null messages when the caller had no conversation history; a serialization bug flattening the array client-side; hand-written curl tests omitting messages.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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