mastra-ai/mastra · error
threadId must be a non-empty string or array of non-empty st
Error message
threadId must be a non-empty string or array of non-empty strings
What it means
InMemoryStorage.listMessages requires threadId to be either a non-empty string or an array of non-empty strings (after trimming whitespace). The method normalizes a single threadId into an array and then validates that the array is non-empty and every element is non-blank. This guard prevents querying messages with an undefined/empty thread set, which would otherwise be ambiguous or accidentally match nothing/all threads.
Source
Thrown at packages/core/src/storage/domains/memory/inmemory.ts:122
}
});
}
async listMessages({
threadId,
resourceId: optionalResourceId,
include,
filter,
perPage: perPageInput,
page = 0,
orderBy,
}: StorageListMessagesInput): Promise<StorageListMessagesOutput> {
const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
// Normalize threadId to array
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
if (threadIds.length === 0 || threadIds.some(id => !id.trim())) {
throw new Error('threadId must be a non-empty string or array of non-empty strings');
}
const threadIdSet = new Set(threadIds);
const { field, direction } = this.parseOrderBy(orderBy, 'ASC');
// Normalize perPage for query (false → MAX_SAFE_INTEGER, 0 → 0, undefined → 40)
const perPage = normalizePerPage(perPageInput, 40);
if (page < 0) {
throw new Error('page must be >= 0');
}
// Prevent unreasonably large page values that could cause performance issues
const maxOffset = Number.MAX_SAFE_INTEGER / 2;
if (page * perPage > maxOffset) {
throw new Error('page value too large');
}View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure a valid thread exists and its id is loaded before calling listMessages.
- Filter out blank entries before passing an array: ids.filter(id => typeof id === 'string' && id.trim().length > 0), then skip the call if the result is empty.
- If threadId may legitimately be absent, guard the call site rather than passing an empty value.
Example fix
// before
await storage.listMessages({ threadId: thread?.id });
// after
if (thread?.id) {
await storage.listMessages({ threadId: thread.id });
} Defensive patterns
Strategy: validation
Validate before calling
function hasValidThreadIds(threadId) {
const ids = Array.isArray(threadId) ? threadId : [threadId];
return ids.length > 0 && ids.every(id => typeof id === 'string' && id.trim().length > 0);
}
if (!hasValidThreadIds(threadId)) return; // skip call or surface input error
await storage.listMessages({ threadId }); Type guard
function isValidThreadId(v: unknown): v is string {
return typeof v === 'string' && v.trim().length > 0;
}
function areValidThreadIds(v: unknown): v is string[] {
return Array.isArray(v) && v.length > 0 && v.every(isValidThreadId);
} Prevention
- Never call listMessages before the thread id is resolved; gate on loaded state.
- Filter blank strings from any array of ids derived from user input.
- Rely on TypeScript types, but still validate at runtime when ids come from external sources (URLs, forms).
When it happens
Trigger: Calling listMessages({ threadId: '' }), threadId: undefined/null (bypassing types), threadId: [], or an array containing blank entries like ['thread-1', ' '] (whitespace-only strings pass a naive check but fail trim()).
Common situations: Thread ID sourced from an uninitialized variable, a route param that is an empty string before the record loads, React effects firing before thread creation completes, or JS callers (no TypeScript) passing null/undefined.
Related errors
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/8c059b083775a442.
Report an issue: GitHub.