mastra-ai/mastra · error

Thread not found

Error message

Thread not found

What it means

When a resourceId is supplied, recallThreadFromStart() verifies thread ownership: it loads the thread via memory.getThreadById({ threadId }) and requires it to exist AND have thread.resourceId === resourceId. If the thread does not exist, or exists under a different resource (user), it throws the generic 'Thread not found' — deliberately vague to avoid leaking cross-resource data.

Source

Thrown at packages/memory/src/tools/om-tools.ts:1146

  limit?: number;
  detail?: RecallDetail;
  partType?: 'text' | 'tool-call' | 'tool-result' | 'reasoning' | 'image' | 'file';
  toolName?: string;
  anchor?: 'start' | 'end';
  maxTokens?: number;
}): Promise<RecallResult> {
  if (!memory) {
    throw new Error('Memory instance is required for recall');
  }
  if (!threadId) {
    throw new Error('Thread ID is required for recall');
  }

  // Verify the thread belongs to the current resource
  if (resourceId && memory.getThreadById) {
    const thread = await memory.getThreadById({ threadId });
    if (!thread || thread.resourceId !== resourceId) {
      throw new Error('Thread not found');
    }
  }

  const MAX_PAGE = 50;
  const MAX_LIMIT = 20;
  const normalizedPage = Math.max(Math.min(page, MAX_PAGE), 1);
  const normalizedLimit = Math.min(Math.max(limit, 1), MAX_LIMIT);
  const pageIndex = normalizedPage - 1;
  const fetchCount = pageIndex * normalizedLimit + normalizedLimit + 1;

  const result = await memory.recall({
    threadId,
    resourceId,
    page: 0,
    perPage: fetchCount,
    orderBy: { field: 'createdAt', direction: anchor === 'end' ? 'DESC' : 'ASC' },
  });

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Confirm the threadId exists: const t = await memory.getThreadById({ threadId }); check t is non-null.
  2. Ensure the resourceId passed at runtime matches the thread's resourceId at creation time.
  3. Re-create the thread with memory.createThread({ threadId, resourceId }) if it was deleted.
  4. In multi-tenant setups, derive resourceId from authenticated context, not client input; omit resourceId if cross-resource browsing is intended (the check only runs when resourceId is provided).

Example fix

// before
await recallThreadFromStart({ memory, threadId: input.threadId, resourceId: userA });
// after
const thread = await memory.getThreadById({ threadId: input.threadId });
if (!thread || thread.resourceId !== userA) {
  return 'Thread not found for this user.';
}
await recallThreadFromStart({ memory, threadId: input.threadId, resourceId: userA });
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check ownership before browsing
const thread = await memory.getThreadById({ threadId });
if (!thread) throw new Error(`Thread ${threadId} does not exist`);
if (resourceId && thread.resourceId !== resourceId) {
  throw new Error(`Thread ${threadId} belongs to resource ${thread.resourceId}`);
}

Type guard

function isAccessibleThread(
  thread: { resourceId: string } | null,
  resourceId?: string
): thread is { resourceId: string } {
  return !!thread && (!resourceId || thread.resourceId === resourceId);
}

Try / catch

try {
  return await recallThreadFromStart({ memory, threadId, resourceId });
} catch (err) {
  if (err instanceof Error && err.message === 'Thread not found') {
    // do not leak whether it was missing vs. forbidden
    return { error: 'Thread not found or not accessible for this user.' };
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling recallThreadFromStart with a threadId that was never created, was deleted, or belongs to a different resourceId; the ownership check at om-tools.ts:1143-1148 then throws.

Common situations: Multi-tenant apps where the wrong user's resourceId is bound to the run; listing thread IDs from an old resource and browsing them under a new one; storage cleared/threads pruned while clients hold stale IDs; typos in thread IDs; passing a resourceId that differs from the one used at thread creation.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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