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
- 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
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
- 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
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
- KnowledgeRecord not found: ${value.recordId}
- MastraClient.deleteThread() requires exactly one of agentId
- No response body
- @mastra/opencode: failed to initialize memory storage from $
- Could not generate title from input ${JSON.stringify(message
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/a89d71cdd035e50f.
Report an issue: GitHub.