mastra-ai/mastra · error
Only `TextNode` is allowed for `Summary` extractor
Error message
Only `TextNode` is allowed for `Summary` extractor
What it means
SummaryExtractor.extract() requires every node in the input array to be an instance of TextNode. Summary generation works on text content, so other node types (or plain objects mimicking nodes) are rejected before any LLM call.
Source
Thrown at packages/rag/src/document/extractors/summary.ts:103
const result = await miniAgent.generateLegacy([{ role: 'user', content: prompt }]);
summary = result.text;
}
if (!summary) {
console.warn('Summary extraction LLM output returned empty');
return '';
}
return summary.replace(STRIP_REGEX, '');
}
/**
* Extract summaries from a list of nodes.
* @param {BaseNode[]} nodes Nodes to extract summaries from.
* @returns {Promise<ExtractSummary[]>} Summaries extracted from the nodes.
*/
async extract(nodes: BaseNode[]): Promise<ExtractSummary[]> {
if (!nodes.every(n => n instanceof TextNode)) throw new Error('Only `TextNode` is allowed for `Summary` extractor');
const nodeSummaries = await Promise.all(nodes.map(node => this.generateNodeSummary(node)));
const metadataList: ExtractSummary[] = nodes.map(() => ({}));
for (let i = 0; i < nodes.length; i++) {
if (i > 0 && this.prevSummary && nodeSummaries[i - 1]) {
metadataList[i]!['prevSectionSummary'] = nodeSummaries[i - 1];
}
if (i < nodes.length - 1 && this.nextSummary && nodeSummaries[i + 1]) {
metadataList[i]!['nextSectionSummary'] = nodeSummaries[i + 1];
}
if (this.selfSummary && nodeSummaries[i]) {
metadataList[i]!['sectionSummary'] = nodeSummaries[i];
}
}
return metadataList;View on GitHub (pinned to 75dd419e61)
Solutions
- Filter the input: nodes.filter(n => n instanceof TextNode) before calling extract.
- If nodes came from JSON, revive them with the proper class (e.g. new TextNode(...)/TextNode.fromJSON) so instanceof passes.
- Split the pipeline so only text-bearing nodes go to the SummaryExtractor.
Example fix
// before await extractor.extract(allNodes); // after await extractor.extract(allNodes.filter(n => n instanceof TextNode));
Defensive patterns
Strategy: type-guard
Validate before calling
const textNodes = nodes.filter((n): n is TextNode => n instanceof TextNode); if (textNodes.length === 0) return [];
Type guard
const isTextNode = (n: BaseNode): n is TextNode => n instanceof TextNode;
Try / catch
try {
return await extractor.extract(nodes);
} catch (e) {
if (e instanceof Error && e.message.includes('Only `TextNode` is allowed')) {
return extractor.extract(nodes.filter(isTextNode));
}
throw e;
} Prevention
- Always narrow node arrays with instanceof TextNode before summary extraction.
- Revise JSON-persisted nodes through their class constructors so instanceof works.
- Keep extraction pipelines type-homogeneous per stage.
When it happens
Trigger: extract(nodes) where nodes mixes TextNode with other BaseNode subclasses (e.g. ImageDocument/ImageNode) or contains objects deserialized as plain JSON without the TextNode prototype.
Common situations: Running extractors over a heterogeneous node list produced by a splitter that emits multiple node types, or passing nodes restored from JSON storage that lost their class identity (no Object.setPrototypeOf / not revived via TextNode.fromJSON).
Related errors
- CursorSDKAgent resumeData.agentId must be a string when prov
- MastraFactory: 'sandbox' must be a function constructing a M
- Factory rules.github.${event}.onEvent must be a function.
- Factory rules.linear.${event}.onEvent must be a function.
- Factory rule decision must be an object.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/2590fbac6bc61c5b.
Report an issue: GitHub.