mastra-ai/mastra · error

Could not resolve cursor message: ${cursor}

Error message

Could not resolve cursor message: ${cursor}

What it means

After trimming, `resolveCursorMessage` looks the cursor up directly via `memoryStore.listMessagesById`, then falls back to scanning the resource's threads via `resolveCursorMessageByRecall`. If no stored message matches the ID, it throws this error. The same message is deliberately reused for access-control failures so callers cannot probe for foreign message IDs.

Source

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

  const rangeIds = parseRangeFormat(normalized);
  if (rangeIds) {
    return {
      hint: `The cursor "${cursor}" looks like a range. Use one of the individual message IDs as the cursor instead: start="${rangeIds.startId}" or end="${rangeIds.endId}".`,
      ...rangeIds,
    };
  }

  const memoryStore = await memory.getMemoryStore();
  const result = await memoryStore.listMessagesById({ messageIds: [normalized] });
  let message = result.messages.find(message => message.id === normalized) ?? null;

  if (!message) {
    message = await resolveCursorMessageByRecall(memory, normalized, access);
  }

  if (!message) {
    throw new Error(`Could not resolve cursor message: ${cursor}`);
  }

  // Verify the cursor message belongs to the current resource
  if (access?.resourceId && message.resourceId !== access.resourceId) {
    throw new Error(`Could not resolve cursor message: ${cursor}`);
  }

  // In strict thread scope, verify the cursor belongs to the current thread
  if (access?.enforceThreadScope && access.threadScope && message.threadId !== access.threadScope) {
    throw new Error(`Could not resolve cursor message: ${cursor}`);
  }

  return message;
}

async function resolveCursorMessageByRecall(
  memory: RecallMemory,
  cursor: string,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Re-list messages for the thread (recall page 1) and copy an exact messageId from the response as the new cursor.
  2. Confirm the resourceId/threadScope passed in the access context matches the thread the cursor came from.
  3. Check the message still exists (thread may have been deleted or trimmed); start a fresh recall if the thread is gone.
  4. Do not use a range cursor (start:end) here — that yields the range-hint error; pick one endpoint ID.

Example fix

// before
await recallMessages({ memory, threadId, cursor: 'msg_from_other_thread' });
// after
const page = await recallMessages({ memory, threadId, page: 1 });
const validId = JSON.parse(page.messages)[0].messageId;
await recallMessages({ memory, threadId, cursor: validId });
Defensive patterns

Strategy: fallback

Validate before calling

// Verify the cursor exists in the thread before using it
const page = await recallMessages({ memory, threadId, resourceId, page: 1 });
const known = JSON.parse(page.messages).some(m => m.messageId === cursor);
if (!known) { cursor = JSON.parse(page.messages).at(-1)?.messageId; }

Try / catch

try {
  return await recallMessages({ memory, threadId, resourceId, cursor });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Could not resolve cursor message')) {
    return recallMessages({ memory, threadId, resourceId, page: 1 }); // restart from the beginning
  }
  throw e;
}

Prevention

When it happens

Trigger: cursor is a message ID that does not exist in the store; the ID belongs to another resource while access.resourceId is set; the ID belongs to a different thread while enforceThreadScope is true; a stale cursor from a deleted thread; a typo'd or hallucinated (LLM-invented) ID.

Common situations: Cursor carried across environments (dev DB id used against prod); memory store wiped or thread garbage-collected between calls; multi-tenant app querying with the wrong resourceId in the access context; an LLM guessing a plausible-looking message ID.

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/ac93331aecb6f908. Report an issue: GitHub.