mastra-ai/mastra · error · KnowledgeConflictError
Knowledge record version conflict: ${id}
Error message
Knowledge record version conflict: ${id} What it means
updateNode performs optimistic concurrency control: the stored node's version must equal input.version. A mismatch means the node was modified (or merged) by someone else since the caller last read it, so the update is rejected with KnowledgeConflictError to prevent lost updates.
Source
Thrown at packages/core/src/storage/domains/knowledge/inmemory.ts:207
node =>
!cursor ||
node.updatedAt < cursor.updatedAt ||
(node.updatedAt.getTime() === cursor.updatedAt.getTime() &&
(node.name > cursor.name || (node.name === cursor.name && node.id > cursor.id))),
)
.slice(0, input.limit ?? 100)
.map(cloneNode);
}
async updateNode(input: UpdateKnowledgeNodeInput): Promise<KnowledgeNode> {
return this.#runAtomicMutation(() => this.#updateNode(input));
}
#updateNode(input: UpdateKnowledgeNodeInput): KnowledgeNode {
assertKnowledgeDescriptionWithinBound(input.description);
const existing = this.#db.knowledgeNodes.get(input.id);
if (!existing) throw new KnowledgeNotFoundError('node', input.id);
if (existing.version !== input.version) throw new KnowledgeConflictError(input.id);
if (existing.mergedInto) throw new Error(`Cannot update merged knowledge node: ${input.id}`);
const scope = canonicalizeKnowledgeScope(input.scope ?? existing.scope);
const name = (input.name ?? existing.name).trim();
const oldKey = recordKey(existing.name, existing.scope);
const newKey = recordKey(name, scope);
const collision = this.#db.knowledgeNodeKeys.get(newKey);
if (collision && collision !== input.id) throw new Error(`Knowledge node already exists in scope: ${name}`);
const updated: KnowledgeNode = {
...existing,
name,
kind: input.kind ?? existing.kind,
content: input.content ?? existing.content,
description: input.description ?? existing.description,
scope,
version: existing.version + 1,
updatedAt: new Date(),View on GitHub (pinned to 75dd419e61)
Solutions
- Re-read the node (getNode) to get the current version and retry the update with it once
- Implement a retry loop around updateNode that refetches the version on KnowledgeConflictError (bounded retries)
- Serialize updates per node (queue/lock) if concurrent writers are common
Example fix
// before
await store.updateNode({ id, version: cachedVersion, description });
// after
let node = await store.getNode({ id, resolutionScope: scope });
for (let i = 0; i < 3; i++) {
try { node = await store.updateNode({ id, version: node.version, description }); break; }
catch (e) { if (e instanceof KnowledgeConflictError) node = await store.getNode({ id, resolutionScope: scope }); else throw e; }
} Defensive patterns
Strategy: retry
Validate before calling
const current = await store.getNode({ id, resolutionScope: scope });
if (current.version !== cachedVersion) {
// refresh your local copy before attempting the update
cachedVersion = current.version;
} Type guard
function isFresh(node: { version: number }, expectedVersion: number): boolean {
return node.version === expectedVersion;
} Try / catch
try {
await store.updateNode({ id, version, ...patch });
} catch (e) {
if (e instanceof KnowledgeConflictError) {
const fresh = await store.getNode({ id, resolutionScope: scope });
await store.updateNode({ id, version: fresh.version, ...patch });
} else throw e;
} Prevention
- Always read the node immediately before updating instead of caching versions
- Bound retries (e.g. 3 attempts) when refetching versions
- Avoid multiple writers updating the same node concurrently; queue writes per node
- Re-apply your patch onto the freshly read node rather than blindly overwriting
When it happens
Trigger: Calling updateNode({ id, version }) where existing.version !== input.version — e.g. two concurrent updates, an update after a mergeNodes bumped the version, or the caller cached a stale version across calls.
Common situations: Multiple agents/workflows editing the same knowledge node concurrently; long-running job holding a version while another request updates the node; retrying an update after a successful first attempt.
Related errors
- Factory kickoff run ended in error.
- Factory kickoff run was aborted before it finished.
- STRUCTURED_OUTPUT_OBJECT_UNDEFINED
- Stale broker election lock removed
- Broker election in progress by another process
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/d645e270733d0391.
Report an issue: GitHub.