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
- Always include a messages array in the request body (may be empty []).
- Validate the payload client-side before the call.
- Check Content-Type is application/json and the body is actually stringified.
- 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
- Build request bodies with a typed client that requires the messages field.
- Always stringify JSON bodies and set Content-Type: application/json.
- Skip the API call entirely when there is nothing to persist.
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
- messageIds is required
- Argument "${key}" is required
- Invalid request index. indexName and vectors array are requi
- runId is required when resumeData is provided
- Messages must be an array of UIMessage objects
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/eb0707611f2c4f06.
Report an issue: GitHub.