mastra-ai/mastra · error
MastraClient.deleteThread() requires exactly one of agentId
Error message
MastraClient.deleteThread() requires exactly one of agentId or networkId. The server cannot resolve which memory store owns the thread without one, and passing both is ambiguous.
What it means
MastraClient.deleteThread() enforces an exact-one-of constraint: callers must pass agentId or networkId, never both and never neither, because the server needs to know which memory store owns the thread. The throw fires when opts is falsy or when the boolean coerced values of agentId and networkId are equal (both absent or both present).
Source
Thrown at client-sdks/client-js/src/client.ts:417
if (opts.networkId) {
url = `/memory/network/threads/${threadId}/messages?networkId=${opts.networkId}${includeSystemRemindersQuery ? `&${includeSystemRemindersQuery}` : ''}${requestContextQueryString(opts.requestContext, includeSystemRemindersQuery ? '&' : '&')}`;
} else if (opts.agentId) {
url = `/memory/threads/${threadId}/messages?agentId=${opts.agentId}${includeSystemRemindersQuery ? `&${includeSystemRemindersQuery}` : ''}${requestContextQueryString(opts.requestContext, '&')}`;
} else {
url = `/memory/threads/${threadId}/messages${includeSystemRemindersQuery ? `?${includeSystemRemindersQuery}` : ''}${requestContextQueryString(opts.requestContext, includeSystemRemindersQuery ? '&' : '?')}`;
}
return this.request(url);
}
public deleteThread(
threadId: string,
opts:
| { agentId: string; networkId?: never; requestContext?: RequestContext | Record<string, any> }
| { networkId: string; agentId?: never; requestContext?: RequestContext | Record<string, any> },
): Promise<{ success: boolean; message: string }> {
if (!opts || !!opts.agentId === !!opts.networkId) {
throw new Error(
'MastraClient.deleteThread() requires exactly one of agentId or networkId. ' +
'The server cannot resolve which memory store owns the thread without one, ' +
'and passing both is ambiguous.',
);
}
const url = opts.agentId
? `/memory/threads/${threadId}?agentId=${opts.agentId}${requestContextQueryString(opts.requestContext, '&')}`
: `/memory/network/threads/${threadId}?networkId=${opts.networkId}${requestContextQueryString(opts.requestContext, '&')}`;
return this.request(url, { method: 'DELETE' });
}
/**
* Saves messages to memory
* @param params - Parameters containing messages to save and optional request context
* @returns Promise containing the saved messages
*/View on GitHub (pinned to 75dd419e61)
Solutions
- Pass exactly one identifier: if the thread belongs to an agent use { agentId }, if it belongs to a network use { networkId }.
- If both values are available in your code, decide ownership explicitly before calling (e.g. prefer networkId when the thread was created by a network).
- Check for empty strings — they are treated as absent and trigger the neither case.
Example fix
// before
await client.deleteThread(threadId, { agentId, networkId });
// after
await client.deleteThread(threadId, networkId ? { networkId } : { agentId }); Defensive patterns
Strategy: validation
Validate before calling
function assertDeleteThreadOpts(opts: { agentId?: string; networkId?: string }) {
const count = [opts.agentId, opts.networkId].filter(Boolean).length;
if (count !== 1) throw new Error('deleteThread requires exactly one of agentId or networkId');
} Type guard
function isDeleteThreadOpts(o: unknown): o is { agentId: string; networkId?: never; requestContext?: unknown } | { networkId: string; agentId?: never; requestContext?: unknown } {
const x = o as any;
return !!x && typeof x === 'object' && ((typeof x.agentId === 'string' && !!x.agentId && !x.networkId) || (typeof x.networkId === 'string' && !!x.networkId && !x.agentId));
} Prevention
- Type thread-owning contexts with discriminated unions so only one ID is reachable.
- Never spread raw objects into deleteThread opts; pick the identifier explicitly.
- Treat empty strings as absent when normalizing IDs.
When it happens
Trigger: Calling client.deleteThread(threadId, {}), omitting opts entirely, or passing { agentId: 'a', networkId: 'n' }; also passing empty-string values which are falsy and count as absent.
Common situations: Migrating code from an older deleteThread(threadId, agentId?) signature and forwarding both identifiers from an agent-network context; spreading an options object that happens to contain both keys.
Related errors
- runId is required to stream an agent builder action
- Thread ID is required for thread-scoped working memory updat
- Resource ID is required for resource-scoped working memory u
- Thread with id ${threadId} resourceId does not match the cur
- Cannot update working memory: ${scope} ID is required
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/6b1ba1c6ca69e8ef.
Report an issue: GitHub.