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
- Ensure each message in the batch uses the resourceId that owns its threadId; remap before sending.
- Split the batch into separate requests per (threadId, resourceId) pair if the data genuinely spans resources.
- If a resource migration caused this, first update the thread's resourceId (PATCH /memory/threads/:threadId) or reassign all messages consistently.
- 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
- Group messages by threadId and assert one resourceId per group before batching.
- After resource migrations, rewrite threadId/resourceId pairs consistently.
- Avoid mixing messages from different sources into one save batch.
- Keep a single source of truth for the thread→resource mapping client-side.
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
- Both threadId or resourceId must be provided
- Agent "${agent.id}" does not have memory configured
- Memory storage is not configured for agent "${agent.id}"
- Observational Memory is not enabled for this agent
- Memory is not configured for this agent
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/d979f0e615fa34dc.
Report an issue: GitHub.