{"record":{"id":"ec434e692394d83b","repo":"run-llama/llama_index","slug":"only-textnode-is-allowed-for-summary-extractor","errorCode":null,"errorMessage":"Only `TextNode` is allowed for `Summary` extractor","messagePattern":"Only `TextNode` is allowed for `Summary` extractor","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/extractors/metadata_extractors.py","lineNumber":427,"sourceCode":"    @classmethod\n    def class_name(cls) -> str:\n        return \"SummaryExtractor\"\n\n    async def _agenerate_node_summary(self, node: BaseNode) -> str:\n        \"\"\"Generate a summary for a node.\"\"\"\n        if self.is_text_node_only and not isinstance(node, TextNode):\n            return \"\"\n\n        context_str = node.get_content(metadata_mode=self.metadata_mode)\n        summary = await self.llm.apredict(\n            PromptTemplate(template=self.prompt_template), context_str=context_str\n        )\n\n        return summary.strip()\n\n    async def aextract(self, nodes: Sequence[BaseNode]) -> List[Dict]:\n        if not all(isinstance(node, TextNode) for node in nodes):\n            raise ValueError(\"Only `TextNode` is allowed for `Summary` extractor\")\n\n        node_summaries_jobs = []\n        for node in nodes:\n            node_summaries_jobs.append(self._agenerate_node_summary(node))\n\n        node_summaries = await run_jobs(\n            node_summaries_jobs,\n            show_progress=self.show_progress,\n            workers=self.num_workers,\n        )\n\n        # Extract node-level summary metadata\n        metadata_list: List[Dict] = [{} for _ in nodes]\n        for i, metadata in enumerate(metadata_list):\n            if i > 0 and self._prev_summary and node_summaries[i - 1]:\n                metadata[\"prev_section_summary\"] = node_summaries[i - 1]\n            if i < len(nodes) - 1 and self._next_summary and node_summaries[i + 1]:\n                metadata[\"next_section_summary\"] = node_summaries[i + 1]","sourceCodeStart":409,"sourceCodeEnd":445,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/extractors/metadata_extractors.py#L409-L445","documentation":"Raised by SummaryExtractor.aextract when any node in the input batch is not a TextNode. Summary extraction runs an LLM prompt over each node's text content, and the async batch path requires every node to be a TextNode (per-node generation does tolerate non-text nodes when is_text_node_only by returning '').","triggerScenarios":"Calling await extractor.aextract(nodes) on a list containing ImageNode, IndexNode, or a custom BaseNode subclass; mixing node types in an ingestion pipeline that feeds all parsed nodes into SummaryExtractor.","commonSituations":"Multimodal pipelines where image/document nodes flow alongside text; custom node classes for specialized stores; converting documents with parsers that emit non-text nodes by default.","solutions":["Filter the batch first: text_nodes = [n for n in nodes if isinstance(n, TextNode)] and run aextract on that list.","Configure the upstream node parser to produce TextNodes only for the content destined for summary extraction.","Handle non-text nodes with a separate extractor/branch appropriate to their type."],"exampleFix":"# before\nresults = await summary_extractor.aextract(nodes)  # mixed node types\n\n# after\nfrom llama_index.core.schema import TextNode\ntext_nodes = [n for n in nodes if isinstance(n, TextNode)]\nresults = await summary_extractor.aextract(text_nodes)","handlingStrategy":"type-guard","validationCode":"from llama_index.core.schema import TextNode\n\ntext_nodes = [n for n in nodes if isinstance(n, TextNode)]\nif len(text_nodes) != len(nodes):\n    logging.warning(\"Filtered %d non-TextNode(s) before summary extraction\", len(nodes) - len(text_nodes))","typeGuard":"from llama_index.core.schema import TextNode, BaseNode\n\ndef all_text_nodes(nodes: list[BaseNode]) -> bool:\n    return all(isinstance(n, TextNode) for n in nodes)","tryCatchPattern":null,"preventionTips":["Filter batches to TextNode before calling aextract.","Keep multimodal/non-text nodes on a separate pipeline branch.","Add isinstance assertions in shared extractor utilities."],"tags":["type-validation","metadata-extraction","async","node-types"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}