mastra-ai/mastra · error · Error

Knowledge merge requires two existing nodes.

Error message

Knowledge merge requires two existing nodes.

What it means

The knowledge_merge tool in the observational-memory subconscious curator loads both the source and target knowledge nodes before merging. If either node cannot be fetched from the knowledge storage domain (it does not exist, or was deleted), the library refuses to merge and throws this error. Merging requires two real, currently existing nodes because the merge rewrites provenance and redirects one node into the other.

Source

Thrown at packages/memory/src/processors/observational-memory/subconscious/knowledge-write-tools.ts:154

    }),
    knowledge_merge_nodes: createTool({
      id: 'knowledge_merge_nodes',
      description: 'Merge a visible duplicate node into another visible node using source-version CAS.',
      inputSchema: {
        type: 'object',
        properties: {
          sourceId: { type: 'string', minLength: 1 },
          targetId: { type: 'string', minLength: 1 },
          sourceVersion: { type: 'integer', minimum: 1 },
        },
        required: ['sourceId', 'targetId', 'sourceVersion'],
        additionalProperties: false,
      } satisfies JSONSchema7,
      execute: async input => {
        const value = input as { sourceId: string; targetId: string; sourceVersion: number };
        const store = await getStore(memory);
        const [source, target] = await Promise.all([store.getNode(value.sourceId), store.getNode(value.targetId)]);
        if (!source || !target) throw new Error('Knowledge merge requires two existing nodes.');
        requireVisible(source.scope, options, 'Knowledge merge source');
        requireVisible(target.scope, options, 'Knowledge merge target');
        return store.mergeNodes(value);
      },
    }),
    knowledge_rescope: createTool({
      id: 'knowledge_rescope',
      description: 'Change a record visibility scope without exceeding its stamped ceiling.',
      inputSchema: {
        type: 'object',
        properties: { recordId: { type: 'string', minLength: 1 }, scope: scopeLevelSchema },
        required: ['recordId', 'scope'],
        additionalProperties: false,
      } satisfies JSONSchema7,
      execute: async input => {
        const value = input as { recordId: string; scope: KnowledgeScopeLevel };
        const store = await getStore(memory);
        const record = await store.getKnowledge({ id: value.recordId });

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Fetch both nodes with store.getNode(sourceId) and store.getNode(targetId) and verify they exist and have no mergedInto before calling merge
  2. Re-resolve node IDs by name with store.resolveNode({ name, scope }) instead of reusing stale IDs
  3. Verify the Memory instance points at the knowledge storage domain that actually holds the nodes

Example fix

// before
await store.mergeNodes({ sourceId: 'node_123', targetId: 'node_456', sourceVersion: 3 });
// after
const source = await store.getNode('node_123');
const target = await store.getNode('node_456');
if (!source || !target || source.mergedInto || target.mergedInto) throw new Error('Both merge nodes must exist and be unmerged');
await store.mergeNodes({ sourceId: source.id, targetId: target.id, sourceVersion: 3 });
Defensive patterns

Strategy: validation

Validate before calling

const [source, target] = await Promise.all([store.getNode(sourceId), store.getNode(targetId)]);
if (!source || !target || source.mergedInto || target.mergedInto) throw new Error('Merge requires two existing, unmerged nodes');

Type guard

function isMergeable(n: { id: string; mergedInto?: string } | null | undefined): n is { id: string; mergedInto?: string } {
  return !!n && !n.mergedInto;
}

Try / catch

try {
  await store.mergeNodes({ sourceId, targetId, sourceVersion });
} catch (e) {
  if (e instanceof Error && e.message.includes('requires two existing nodes')) {
    // re-resolve nodes by name and retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the knowledge_merge tool with a sourceId or targetId that does not exist in knowledge storage, that references a node already merged into another node (mergedInto set), or with IDs from a different knowledge store than the one configured on the Memory instance.

Common situations: An LLM curator hallucinating node IDs; passing IDs captured before a storage wipe or migration; merging across dev/staging databases; using IDs from the wrong thread's scope after data cleanup.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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