mastra-ai/mastra · error
Thread not found: ${overrides.threadId}
Error message
Thread not found: ${overrides.threadId} What it means
createSessionForResource was asked to open a thread by overrides.threadId, the thread exists in storage, but its resourceId does not match the effective resource id of the caller. Since a thread is scoped to a single resource, a thread belonging to another resource is treated as 'not found' rather than leaked across resource boundaries. This protects multi-tenant isolation of conversations.
Source
Thrown at packages/core/src/agent-controller/agent-controller.ts:696
new Session({
resourceId: effectiveResourceId,
id,
ownerId,
tags,
state: {
initialState,
stateSchema: this.config.stateSchema,
},
workspace: workspaceToConnect,
browser: browserToConnect,
}),
);
if (overrides?.threadId) {
const existingThread = await session.thread.getById({ threadId: overrides.threadId });
if (existingThread) {
if (existingThread.resourceId !== effectiveResourceId) {
throw new Error(`Thread not found: ${overrides.threadId}`);
}
await this.config.threadLock?.acquire(existingThread.id);
session.thread.set({ threadId: existingThread.id });
await session.thread.loadMetadata();
await session.thread.ensureCurrentSubscription();
} else {
await session.thread.create({ id: overrides.threadId });
}
} else {
// Same scope `thread.create()` stamps, matched strictly: a thread outside
// this session's scope — including one carrying no scope at all — belongs
// to nobody here and must not be auto-resumed.
const scopeEntries = Object.entries(session.getThreadScope());
const threads = await session.thread.list();
const candidates = threads.filter(t => {
const metadata = (t.metadata as Record<string, unknown> | undefined) ?? {};
return scopeEntries.every(([key, value]) => metadata[key] === value);View on GitHub (pinned to 75dd419e61)
Solutions
- Pass the correct resourceId that matches the thread's stored resourceId, or omit overrides.threadId to create a fresh thread
- Verify how effectiveResourceId is derived and ensure it is identical to the one used when the thread was created (stable derivation, no case/format drift)
- If the thread genuinely belongs to another resource, look it up under that resource instead of forcing a cross-resource access
- Check storage for the thread's actual resourceId (e.g. via memory storage getThreadById) to confirm the mismatch
Example fix
// before
await controller.createSessionForResource({ overrides: { threadId: threadIdFromOtherUser } });
// after
const thread = await memoryStorage.getThreadById({ threadId: threadIdFromOtherUser });
if (thread?.resourceId === currentResourceId) {
await controller.createSessionForResource({ overrides: { threadId: threadIdFromOtherUser } });
} else {
await controller.createSessionForResource({}); // fresh thread
} Defensive patterns
Strategy: validation
Validate before calling
const thread = await memoryStorage.getThreadById({ threadId });
if (thread && thread.resourceId !== currentResourceId) throw new Error('thread belongs to another resource'); Try / catch
try {
await controller.createSessionForResource({ overrides: { threadId } });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Thread not found:')) {
// fall back to a fresh thread
return controller.createSessionForResource({});
}
throw e;
} Prevention
- Derive resourceId with a stable, deterministic function shared by create and lookup paths
- Never accept raw threadIds from client input without verifying ownership against the authenticated resource
- Store resourceId alongside threadId in client-side caches and revalidate before reuse
When it happens
Trigger: Calling an API that accepts overrides.threadId (e.g. session creation / resume) with a threadId whose persisted resourceId differs from the effectiveResourceId computed from the request (config.resourceId / request context). Also occurs when resourceId is recomputed differently (e.g. changed casing/format) between the thread's creation and the lookup.
Common situations: Multi-user apps passing one user's threadId while authenticating as another user; changing resourceId derivation logic after threads were created; storing threadIds client-side and reusing them after a resourceId config change.
Related errors
- Thread not found: ${threadId}
- Thread with id ${threadId} is for resource with id ${thread.
- Session is not available to the current user
- Factory session not found
- Factory session ${session.sessionId} is not available to the
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1984148ee7f766cf.
Report an issue: GitHub.