mastra-ai/mastra · error · HTTPException

All messages for the same threadId must use the same resourc

Error message

All messages for the same threadId must use the same resourceId.

What it means

Within a single save-messages call, every message carrying the same threadId must also carry the same resourceId. The handler builds a threadId→resourceId map and throws HTTP 400 as soon as a second message reuses a threadId with a different resourceId, preventing a thread from being split across resources.

Source

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

        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);
        if (!existingResourceId) {
          resourceIdByThread.set(message.threadId, message.resourceId);
        } else if (existingResourceId !== message.resourceId) {
          throw new HTTPException(400, {
            message: 'All messages for the same threadId must use the same resourceId.',
          });
        }
      }

      // Validate that all messages have threadId and resourceId
      const invalidMessages = incomingMessages.filter(message => !message.threadId || !message.resourceId);
      if (invalidMessages.length > 0) {
        throw new HTTPException(400, {
          message: `All messages must have threadId and resourceId fields. Found ${invalidMessages.length} invalid message(s).`,
        });
      }

      // If effectiveResourceId is set, validate all messages belong to this resource
      if (effectiveResourceId) {
        const unauthorizedMessages = incomingMessages.filter(message => message.resourceId !== effectiveResourceId);
        if (unauthorizedMessages.length > 0) {
          throw new HTTPException(403, {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Ensure each message in the batch uses the resourceId that owns its threadId; remap before sending.
  2. Split the batch into separate requests per (threadId, resourceId) pair if the data genuinely spans resources.
  3. If a resource migration caused this, first update the thread's resourceId (PATCH /memory/threads/:threadId) or reassign all messages consistently.
  4. Deduplicate/reconcile client-side by grouping messages by threadId and verifying one resourceId per group.

Example fix

// before
messages = [{ threadId: 't1', resourceId: 'user-a', ... }, { threadId: 't1', resourceId: 'user-b', ... }];
// after
messages = messages.map(m => ({ ...m, resourceId: 'user-a' })); // normalize batch to a single resourceId per thread
Defensive patterns

Strategy: validation

Validate before calling

const byThread = new Map<string, string>();
for (const m of messages) {
  const prev = byThread.get(m.threadId);
  if (prev && prev !== m.resourceId) {
    throw new Error(`threadId ${m.threadId} maps to multiple resourceIds: ${prev}, ${m.resourceId}`);
  }
  byThread.set(m.threadId, m.resourceId);
}

Try / catch

try {
  await saveMessages({ messages });
} catch (e) {
  if (isHttpError(e) && e.status === 400 && e.message.includes('same resourceId')) {
    // regroup batch: one request per (threadId, resourceId) pair
  } else throw e;
}

Prevention

When it happens

Trigger: POST /memory/save-messages with a batch like [{threadId:'t1', resourceId:'r1', ...}, {threadId:'t1', resourceId:'r2', ...}] — same threadId appears with two distinct resourceIds in one payload.

Common situations: Batch-saving conversation history after a user/resource migration where some messages still carry the old resourceId; copying messages between resources without rewriting threadIds; concurrent writers with stale resourceId caches assembling a combined batch.

Related errors


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