mastra-ai/mastra · error

Cannot update merged knowledge node: ${input.id}

Error message

Cannot update merged knowledge node: ${input.id}

What it means

The target node has already been merged into another node (mergedInto is set). Merged nodes are tombstones — their content is owned by the merge target — so updating them is forbidden to avoid diverging from the terminal node.

Source

Thrown at packages/core/src/storage/domains/knowledge/inmemory.ts:208

          !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

  1. Resolve the merge chain and update the terminal (target) node instead of the tombstone
  2. Skip the update if the node is merged (treat as already superseded)
  3. Re-check freshness with getNode before updating, and handle mergedInto explicitly in your workflow

Example fix

// before
await store.updateNode({ id: sourceId, version, description });
// after
const node = await store.getNode({ id: sourceId, resolutionScope: scope });
const target = node.mergedInto ?? node.id;
await store.updateNode({ id: target, version: (await store.getNode({ id: target, resolutionScope: scope })).version, description });
Defensive patterns

Strategy: type-guard

Validate before calling

const node = await store.getNode({ id, resolutionScope: scope });
if (node.mergedInto) throw new Error(`node ${id} was merged into ${node.mergedInto}; update the target instead`);

Type guard

function isMerged(node: KnowledgeNode): boolean {
  return typeof node.mergedInto === 'string' && node.mergedInto.length > 0;
}

Try / catch

try {
  await store.updateNode({ id, version, ...patch });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Cannot update merged knowledge node')) {
    const terminal = await resolveTerminal(id);
    await store.updateNode({ id: terminal.id, version: terminal.version, ...patch });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling updateNode({ id }) on a node that a previous mergeNodes(sourceId=id, targetId=...) call marked as merged, while its stale version still passes the version check (or the caller replayed an old update).

Common situations: A workflow captured the node before a merge happened elsewhere; retry logic re-applying an old update; agents sharing a knowledge store where one merged a duplicate into another.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/4118554cdefbce92. Report an issue: GitHub.