mastra-ai/mastra · error · Error

Current thread is required when browsing by cursor

Error message

Current thread is required when browsing by cursor

What it means

When browsing messages by cursor (message ID) without a resolved threadId, non-resource-scoped memory has no way to locate the thread containing the cursor — it is limited to the current thread. Mastra throws because a current thread is mandatory in that path.

Source

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

        if (!memory.getThreadById) {
          throw new Error('Memory instance cannot verify thread access for recall');
        }

        const thread = await memory.getThreadById({ threadId: resolvedThreadId! });
        if (!thread || thread.resourceId !== resourceId) {
          throw new Error('Thread does not belong to the active resource');
        }

        targetThreadId = thread.id;
        threadScope = thread.id;
      } else {
        targetThreadId = currentThreadId;
        threadScope = currentThreadId || undefined;
      }

      if (hasCursor && !hasResolvedThreadId && !currentThreadId) {
        if (!isResourceScope) {
          throw new Error('Current thread is required when browsing by cursor');
        }

        const resolved = await resolveCursorMessage(memory, cursor!, { resourceId });
        if ('hint' in resolved) {
          return {
            messages: resolved.hint,
            count: 0,
            cursor: cursor!,
            page: page ?? 1,
            limit: Math.min(limit ?? 20, 20),
            detail: detail ?? 'low',
            hasNextPage: false,
            hasPrevPage: false,
            truncated: false,
            tokenOffset: 0,
          };
        }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass threadId along with the cursor in the tool context.
  2. Enable resource scope so the cursor can be resolved across threads via resolveCursorMessage.
  3. Maintain the currentThreadId in the run context (invoke within an agent run that has a thread).

Example fix

// before
await recallTool.execute({ context: { mode: 'messages', cursor: 'msg-42' } });
// after
await recallTool.execute({ context: { mode: 'messages', cursor: 'msg-42', threadId: 'thread-abc' } });
Defensive patterns

Strategy: validation

Validate before calling

if (cursor && !threadId && !currentThreadId) {
  throw new Error('threadId or an active thread is required when paginating with a cursor');
}

Type guard

function cursorCallIsValid(a: { cursor?: string; threadId?: string; currentThreadId?: string }): boolean {
  return !a.cursor || Boolean(a.threadId || a.currentThreadId);
}

Try / catch

try {
  return await recall({ mode: 'messages', cursor, threadId });
} catch (err) {
  if (err instanceof Error && err.message === 'Current thread is required when browsing by cursor') {
    return recall({ mode: 'messages', threadId: knownThreadId, cursor });
  }
  throw err;
}

Prevention

When it happens

Trigger: mode="messages" with a cursor but no threadId and no currentThreadId, while resource scope is disabled (so cursor-based thread resolution via resolveCursorMessage isn't used).

Common situations: Paginating history via cursor in a standalone script with no active agent thread; cursor continuation calls that drop threadId between pages.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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