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

  1. Pass the correct resourceId that matches the thread's stored resourceId, or omit overrides.threadId to create a fresh thread
  2. 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)
  3. If the thread genuinely belongs to another resource, look it up under that resource instead of forcing a cross-resource access
  4. 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

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


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