mastra-ai/mastra · error
Cannot merge a knowledge node into a target that is narrower
Error message
Cannot merge a knowledge node into a target that is narrower than its source scope
What it means
mergeNodes() enforces that a node can only be merged into a target whose scope is visible from (i.e., at least as wide as) the source's scope. If isKnowledgeScopeVisible(target.scope, source.scope) is false, the target scope is narrower than the source scope, and merging would silently narrow the visibility of the source's records. The library refuses before moving any knowledge records.
Source
Thrown at packages/core/src/storage/domains/knowledge/inmemory.ts:258
if (record.node !== input.id) continue;
this.#enqueue('record', record.id, 'delete', createKnowledgeUlid(), record.scope);
if (!record.deletedAt) this.#enqueue('record', record.id, 'upsert', createKnowledgeUlid(), record.scope);
}
}
this.#enqueue('node', input.id, 'upsert', updated.version, scope);
return cloneNode(updated);
}
async mergeNodes(input: { sourceId: string; targetId: string; sourceVersion: number }): Promise<KnowledgeNode> {
if (input.sourceId === input.targetId) throw new Error('Cannot merge a knowledge node into itself');
const source = this.#db.knowledgeNodes.get(input.sourceId);
if (!source) throw new KnowledgeNotFoundError('node', input.sourceId);
if (source.version !== input.sourceVersion) throw new KnowledgeConflictError(input.sourceId);
const target = this.#resolveTerminalNode(input.targetId);
if (!target) throw new KnowledgeNotFoundError('node', input.targetId);
if (target.id === source.id) throw new Error('Cannot create a knowledge merge cycle');
if (!isKnowledgeScopeVisible(target.scope, source.scope)) {
throw new Error('Cannot merge a knowledge node into a target that is narrower than its source scope');
}
for (const [id, record] of this.#db.knowledgeRecords) {
if (record.node === source.id) {
this.#db.knowledgeRecords.set(id, { ...record, node: target.id });
this.#enqueue('record', id, record.deletedAt ? 'delete' : 'upsert', createKnowledgeUlid(), record.scope);
}
}
for (const [key, mentions] of this.#db.knowledgeMentions) {
if (mentions.has(source.id)) {
const next = new Set(mentions);
next.delete(source.id);
next.add(target.id);
this.#db.knowledgeMentions.set(key, next);
const separator = key.indexOf(':');
const sourceType = key.slice(0, separator);
const sourceId = key.slice(separator + 1);
if (sourceType === 'record') {View on GitHub (pinned to 75dd419e61)
Solutions
- Pick a merge target whose scope is equal to or wider than the source node's scope.
- Rescope the source node/records to the target's scope first (if the ceiling allows), then merge.
- If the target must be narrower, raise the target's scope instead of merging the wider node into it.
- Pre-filter merge candidates by comparing knowledge scope keys before submitting merges in bulk jobs.
Example fix
// before
await storage.mergeNodes({ sourceId: wide.id, sourceVersion, targetId: narrow.id }); // target scope narrower
// after
const candidates = allNodes.filter(n => isKnowledgeScopeVisible(n.scope, wide.scope) && n.id !== wide.id);
await storage.mergeNodes({ sourceId: wide.id, sourceVersion, targetId: candidates[0].id }); Defensive patterns
Strategy: validation
Validate before calling
const source = await storage.getKnowledgeNode(sourceId);
const target = await storage.getKnowledgeNode(targetId);
if (source && target && !isKnowledgeScopeVisible(target.scope, source.scope)) {
throw new SkipMerge('target scope narrower than source scope');
} Type guard
function canMergeIntoScope(sourceScope: KnowledgeScope, targetScope: KnowledgeScope): boolean {
return isKnowledgeScopeVisible(targetScope, sourceScope);
} Try / catch
try {
await storage.mergeNodes(input);
} catch (e) {
if (e.message.includes('narrower than its source scope')) {
// choose a wider target or rescope first
return null;
}
throw e;
} Prevention
- Filter merge candidates with isKnowledgeScopeVisible before submitting.
- Keep scope taxonomies consistent across agents creating knowledge.
- Rescope records first if consolidation requires a narrower target.
- Log skipped merges so scope mismatches are visible.
When it happens
Trigger: Calling mergeNodes() where source.scope is e.g. {org, project} and target.scope is a narrower subset like {org, project, agent} (or a different tenant), so records moved to the target would escape their original visibility ceiling.
Common situations: Merging nodes created by different agents with different scopes; refactoring scope taxonomies (renamed agents/projects) so old nodes no longer nest correctly; a dedupe job matching nodes by text similarity across scopes.
Related errors
- Invalid knowledge scope entry: ${entry}
- Knowledge scope contains multiple ${level} entries
- Knowledge scope cannot be empty
- Thread knowledge scope requires resource and org ancestors
- Resource knowledge scope requires an org ancestor
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/fbd8d33a5a790139.
Report an issue: GitHub.