mastra-ai/mastra · error · HTTPException

Messages are required

Error message

Messages are required

What it means

After memory is resolved, the save-messages handler checks that a messages payload was supplied. A falsy messages value throws HTTPException(400, 'Messages are required').

Source

Thrown at packages/server/src/server/handlers/memory.ts:1301

  responseType: 'json',
  queryParamSchema: agentIdQuerySchema,
  bodySchema: saveMessagesBodySchema,
  responseSchema: saveMessagesResponseSchema,
  summary: 'Save messages',
  description: 'Saves new messages to memory',
  tags: ['Memory'],
  requiresAuth: true,
  handler: async ({ mastra, agentId, messages, requestContext }) => {
    try {
      const effectiveResourceId = getEffectiveResourceId(requestContext, undefined);
      const memory = await getMemoryFromContext({ mastra, agentId, requestContext });

      if (!memory) {
        throw new HTTPException(400, { message: 'Memory is not initialized' });
      }

      if (!messages) {
        throw new HTTPException(400, { message: 'Messages are required' });
      }

      if (!Array.isArray(messages)) {
        throw new HTTPException(400, { message: 'Messages should be an array' });
      }

      // The body schema is intentionally permissive (unknown[]); narrow to the
      // fields this handler validates and normalizes.
      const incomingMessages = messages as Array<
        { id?: string; threadId?: string; resourceId?: string; createdAt?: string | Date } & Record<string, unknown>
      >;

      const resourceIdByThread = new Map<string, string>();
      for (const message of incomingMessages) {
        if (!message.threadId || !message.resourceId) {
          continue;
        }
        const existingResourceId = resourceIdByThread.get(message.threadId);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Always include a messages array in the request body (may be empty []).
  2. Validate the payload client-side before the call.
  3. Check Content-Type is application/json and the body is actually stringified.
  4. If no messages should be saved, don't call the endpoint at all.

Example fix

// before
await fetch(url, { method: 'POST' }); // no body
// after
await fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ threadId, messages }),
});
Defensive patterns

Strategy: validation

Validate before calling

if (!Array.isArray(messages)) {
  throw new TypeError('messages must be an array before calling saveMessages');
}

Type guard

function isMessageArray(v: unknown): v is Message[] {
  return Array.isArray(v) && v.every(m => !!m && typeof m === 'object' && typeof (m as any).role === 'string');
}

Try / catch

try {
  await saveMessages({ threadId, messages });
} catch (e) {
  if (isHttpException(e, 400) && /messages are required/i.test(e.message)) {
    logger.warn('saveMessages called without a payload; check request construction');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST to the save-messages endpoint with an omitted or undefined messages field in the body.

Common situations: Client sends an empty body or only threadId/resourceId; a build/serialization step drops the messages key; fetch called without JSON body.

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/eb0707611f2c4f06. Report an issue: GitHub.