mastra-ai/mastra · error · Error
expectedVersion is only valid for an existing node.
Error message
expectedVersion is only valid for an existing node.
What it means
The node upsert tool uses expectedVersion for optimistic concurrency on existing nodes. If the (name, scope) does not resolve to an existing node, the call becomes a create, and expectedVersion is meaningless there — the library rejects the combination instead of ignoring it. This protects callers from thinking they are updating a node that actually does not exist yet.
Source
Thrown at packages/memory/src/processors/observational-memory/subconscious/knowledge-write-tools.ts:254
kind?: string;
content: string;
scope?: KnowledgeScopeLevel;
expectedVersion?: number;
};
const trimmedName = value.name.trim();
const reservedName = trimmedName.toLowerCase();
const name = reservedName === 'capture-guidance' ? reservedName : trimmedName;
if (reservedName === 'capture-guidance' && value.content.length > MAX_GUIDANCE_LENGTH) {
throw new Error(`capture-guidance is limited to ${MAX_GUIDANCE_LENGTH} characters.`);
}
const store = await getStore(memory);
const scope = resolveWriteScope(options, value.scope);
const resolvedNode = await store.resolveNode({ name, scope });
const existing =
resolvedNode && knowledgeScopeKey(resolvedNode.scope) === knowledgeScopeKey(scope) ? resolvedNode : null;
if (!existing) {
if (value.expectedVersion !== undefined)
throw new Error('expectedVersion is only valid for an existing node.');
return store.createNode({
name,
kind: value.kind ?? 'document',
content: value.content,
scope,
resolutionScope: options.scope,
});
}
if (value.expectedVersion === undefined) throw new Error('Updating node content requires expectedVersion.');
return store.updateNode({
id: existing.id,
version: value.expectedVersion,
kind: value.kind,
content: value.content,
resolutionScope: options.scope,
});
},
}),View on GitHub (pinned to 75dd419e61)
Solutions
- Omit expectedVersion to create the node, or first create the node then update with expectedVersion
- Verify the node exists with store.resolveNode({ name, scope }) and that the scope matches the node's scope key
- Correct the node name or scope level so it resolves to the existing node
Example fix
// before
await tool.execute({ name: 'typo-name', content, expectedVersion: 2 });
// after
const resolved = await store.resolveNode({ name: 'node-name', scope });
if (resolved) await tool.execute({ name: 'node-name', content, expectedVersion: resolved.version });
else await tool.execute({ name: 'node-name', content }); Defensive patterns
Strategy: validation
Validate before calling
const resolved = await store.resolveNode({ name, scope });
if (!resolved && expectedVersion !== undefined) {
delete input.expectedVersion; // create path
} Try / catch
try {
await tool.execute(input);
} catch (e) {
if (e instanceof Error && e.message.includes('expectedVersion is only valid for an existing node')) {
const { expectedVersion, ...createInput } = input;
await tool.execute(createInput); // retry as create
} else throw e;
} Prevention
- Resolve the node first and branch create/update explicitly
- Only pass expectedVersion when the node is known to exist
- Match scope levels exactly so resolveNode finds the node
When it happens
Trigger: Passing expectedVersion with a name that has no node in the target scope — e.g. a typo'd node name, wrong scope level, or a node that was deleted or merged so resolveNode no longer matches within the same scope key.
Common situations: First-time writes to a node assumed to exist; scope mismatch so resolveNode looks in a different scope than where the node lives; concurrent merge removed the node between planning and execution.
Related errors
- Updating node content requires expectedVersion.
- MastraClient.deleteThread() requires exactly one of agentId
- 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
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/e3bc4c884dee743f.
Report an issue: GitHub.