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
- Re-list messages for the thread (recall page 1) and copy an exact messageId from the response as the new cursor.
- Confirm the resourceId/threadScope passed in the access context matches the thread the cursor came from.
- Check the message still exists (thread may have been deleted or trimmed); start a fresh recall if the thread is gone.
- 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
- Store cursors with the resourceId/threadId they were issued for.
- Never let the model invent message IDs; require verbatim reuse from tool output.
- Handle deleted/trimmed threads by restarting recall from page 1.
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
- Cursor is required
- The cursor "${cursor}" looks like a range. Use one of the in
- Message ${cursor} has no visible content (it may be an inter
- AGENT_NETWORK_OBSERVATIONAL_MEMORY_UNSUPPORTED
- Observational memory record not found: ${id}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ac93331aecb6f908.
Report an issue: GitHub.