{"record":{"id":"a89d71cdd035e50f","repo":"mastra-ai/mastra","slug":"knowledge-merge-requires-two-existing-nodes","errorCode":null,"errorMessage":"Knowledge merge requires two existing nodes.","messagePattern":"Knowledge merge requires two existing nodes\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/subconscious/knowledge-write-tools.ts","lineNumber":154,"sourceCode":"    }),\n    knowledge_merge_nodes: createTool({\n      id: 'knowledge_merge_nodes',\n      description: 'Merge a visible duplicate node into another visible node using source-version CAS.',\n      inputSchema: {\n        type: 'object',\n        properties: {\n          sourceId: { type: 'string', minLength: 1 },\n          targetId: { type: 'string', minLength: 1 },\n          sourceVersion: { type: 'integer', minimum: 1 },\n        },\n        required: ['sourceId', 'targetId', 'sourceVersion'],\n        additionalProperties: false,\n      } satisfies JSONSchema7,\n      execute: async input => {\n        const value = input as { sourceId: string; targetId: string; sourceVersion: number };\n        const store = await getStore(memory);\n        const [source, target] = await Promise.all([store.getNode(value.sourceId), store.getNode(value.targetId)]);\n        if (!source || !target) throw new Error('Knowledge merge requires two existing nodes.');\n        requireVisible(source.scope, options, 'Knowledge merge source');\n        requireVisible(target.scope, options, 'Knowledge merge target');\n        return store.mergeNodes(value);\n      },\n    }),\n    knowledge_rescope: createTool({\n      id: 'knowledge_rescope',\n      description: 'Change a record visibility scope without exceeding its stamped ceiling.',\n      inputSchema: {\n        type: 'object',\n        properties: { recordId: { type: 'string', minLength: 1 }, scope: scopeLevelSchema },\n        required: ['recordId', 'scope'],\n        additionalProperties: false,\n      } satisfies JSONSchema7,\n      execute: async input => {\n        const value = input as { recordId: string; scope: KnowledgeScopeLevel };\n        const store = await getStore(memory);\n        const record = await store.getKnowledge({ id: value.recordId });","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/subconscious/knowledge-write-tools.ts#L136-L172","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fetch both nodes with store.getNode(sourceId) and store.getNode(targetId) and verify they exist and have no mergedInto before calling merge","Re-resolve node IDs by name with store.resolveNode({ name, scope }) instead of reusing stale IDs","Verify the Memory instance points at the knowledge storage domain that actually holds the nodes"],"exampleFix":"// before\nawait store.mergeNodes({ sourceId: 'node_123', targetId: 'node_456', sourceVersion: 3 });\n// after\nconst source = await store.getNode('node_123');\nconst target = await store.getNode('node_456');\nif (!source || !target || source.mergedInto || target.mergedInto) throw new Error('Both merge nodes must exist and be unmerged');\nawait store.mergeNodes({ sourceId: source.id, targetId: target.id, sourceVersion: 3 });","handlingStrategy":"validation","validationCode":"const [source, target] = await Promise.all([store.getNode(sourceId), store.getNode(targetId)]);\nif (!source || !target || source.mergedInto || target.mergedInto) throw new Error('Merge requires two existing, unmerged nodes');","typeGuard":"function isMergeable(n: { id: string; mergedInto?: string } | null | undefined): n is { id: string; mergedInto?: string } {\n  return !!n && !n.mergedInto;\n}","tryCatchPattern":"try {\n  await store.mergeNodes({ sourceId, targetId, sourceVersion });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('requires two existing nodes')) {\n    // re-resolve nodes by name and retry once\n  } else throw e;\n}","preventionTips":["Always obtain node IDs from resolveNode or fresh getNode calls, never from prompt history","Check mergedInto before merge/update operations","Use one Memory instance (and storage backend) per environment"],"tags":["memory","knowledge-graph","entity-not-found"],"backgroundTag":"entity-not-found","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}