mastra-ai/mastra · error
Cursor is required
Error message
Cursor is required
What it means
The observational-memory recall tooling resolves a `cursor` parameter to a specific message before reading context around it. `resolveCursorMessage` first trims the cursor and throws immediately if the result is empty, because an empty cursor cannot identify any message. This is an input-validation guard: callers must pass a non-empty, single message ID string.
Source
Thrown at packages/memory/src/tools/om-tools.ts:128
// Colon-delimited range: "startId:endId"
const colonIndex = cursor.indexOf(':');
if (colonIndex > 0 && colonIndex < cursor.length - 1) {
return { startId: cursor.slice(0, colonIndex), endId: cursor.slice(colonIndex + 1) };
}
return null;
}
async function resolveCursorMessage(
memory: RecallMemory,
cursor: string,
access?: { resourceId?: string; threadScope?: string; enforceThreadScope?: boolean },
): Promise<MastraDBMessage | { hint: string; startId: string; endId: string }> {
const normalized = cursor.trim();
if (!normalized) {
throw new Error('Cursor is required');
}
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);
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Pass a valid non-empty message ID as the cursor; use the messageId returned by a previous recall/list call.
- If there is no cursor to continue from, omit the cursor-dependent call and use plain paged recall (page/limit) instead.
- Validate the cursor before calling: if (!cursor?.trim()) skip or default the call.
- If the model produced the empty value, improve the tool description/examples so the agent knows to copy an exact messageId.
Example fix
// before
await recallMessages({ memory, threadId, cursor: userCursor });
// after
if (!userCursor?.trim()) {
throw new Error('No cursor provided; start with paged recall instead.');
}
await recallMessages({ memory, threadId, cursor: userCursor.trim() }); Defensive patterns
Strategy: validation
Validate before calling
function hasCursor(cursor: unknown): cursor is string {
return typeof cursor === 'string' && cursor.trim().length > 0;
}
if (!hasCursor(cursor)) throw new Error('Provide a non-empty messageId cursor or omit the call.'); Type guard
function isNonEmptyString(v: unknown): v is string {
return typeof v === 'string' && v.trim().length > 0;
} Try / catch
try {
await recallMessages({ memory, threadId, cursor });
} catch (e) {
if (e instanceof Error && e.message === 'Cursor is required') {
// fall back to paged recall from page 1
return recallMessages({ memory, threadId, page: 1 });
}
throw e;
} Prevention
- Only pass cursors copied verbatim from prior tool responses.
- Treat cursor as optional in app code and switch to paged recall when absent.
- Whitelist the tool argument so an LLM cannot submit an empty string.
When it happens
Trigger: Calling recall/recallMessages or any om-tool path that passes cursor="" or cursor consisting only of whitespace (e.g. an unset variable interpolated as empty string, cursor: '' from a tool schema default).
Common situations: A model (LLM) fills in the cursor argument with an empty string because it has no previous recall result to continue from; an app passes an optional cursor variable that was never initialized; a client omits the field and downstream code coerces undefined to ''.
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
- Could not resolve cursor message: ${cursor}
- The cursor "${cursor}" looks like a range. Use one of the in
- Message ${cursor} has no visible content (it may be an inter
- CursorSDKAgent resumeData must include a message.
- CursorSDKAgent resumeData.agentId must be a string when prov
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/0a3dbb512303897e.
Report an issue: GitHub.