Repository navigation
RSS/Atom converter silently truncates content to the first child node, dropping CDATA sections #2461
Description
The Bug
RssConverter._get_data_by_tag_name (packages/markitdown/src/markitdown/converters/_rss_converter.py, lines 308-321), the shared helper used to extract title, description, pubDate, content:encoded (RSS) and title, subtitle, updated, summary/content (Atom), only reads the first child node of a matched element instead of concatenating all of its text-bearing children:
def _get_data_by_tag_name(self, element, tag_name):
"""Get data from first child element with the given tag name.
Returns None when no such element is found.
"""
nodes = element.getElementsByTagName(tag_name)
if not nodes:
return None
fc = nodes[0].firstChild
if fc:
if hasattr(fc, "data"):
return fc.data
return NoneIn xml.dom.minidom, an element's children are not automatically merged. Any element that mixes plain text with a <![CDATA[...]]> section, or just has whitespace indentation before a CDATA block (extremely common in pretty-printed feeds), produces multiple sibling child nodes. Only the first one is ever read - everything after it is silently dropped, with no error or warning, and the leftover whitespace/text is still truthy so calling code has no signal anything went wrong.
Steps to Reproduce
Case 1, whitespace-indented CDATA (the more dangerous variant - the result is truthy and looks superficially fine):
from xml.dom import minidom
xml_bytes = b"""<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>
<![CDATA[My Real Channel Title]]>
</title>
<description>Feed description</description>
</channel>
</rss>
"""
doc = minidom.parseString(xml_bytes)
def get_data_by_tag_name(element, tag_name):
nodes = element.getElementsByTagName(tag_name)
if not nodes:
return None
fc = nodes[0].firstChild
if fc and hasattr(fc, "data"):
return fc.data
return None
channel = doc.getElementsByTagName("channel")[0]
print(repr(get_data_by_tag_name(channel, "title")))Actual: '\n ' - the real title "My Real Channel Title" (held in the CDATASection sibling) is completely lost. _parse_rss_type's if channel_title: md_text += f"# {channel_title}\n" still fires (the whitespace string is truthy) and emits a heading containing only whitespace.
Expected: "My Real Channel Title".
Case 2, mixed text/CDATA content:
<description>Intro text before html. <![CDATA[<p>Rich HTML fragment that matters.</p>]]> Trailing text after html too.</description>Actual: 'Intro text before html. ' - both the CDATA HTML fragment and the trailing sentence are dropped.
Expected: all three pieces concatenated.
Fix direction
Concatenate every text-bearing child instead of returning only firstChild.data, e.g.:
from xml.dom.minidom import Node
def _get_data_by_tag_name(self, element, tag_name):
nodes = element.getElementsByTagName(tag_name)
if not nodes:
return None
parts = [
child.data
for child in nodes[0].childNodes
if child.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE)
]
return "".join(parts).strip() if parts else NoneDuplicate check
Searched RSS+CDATA, firstChild, RSS+truncat, getElementsByTagName, nodeValue, silently+truncat (all 0 results), plus broader RSS+description (12 hits) and rss+converter (4 hits) whose bodies were read in full - all about different mechanisms: double-HTML-parsing losing literal angle brackets (#2373-2375), Atom XHTML preservation (#2297), a RecursionError fallback (#2333, already implemented), an UnboundLocalError on missing title (#1946, #1989, #2164), RSS metadata leakage plus an unrelated pptx fix (#1785), and two multi-bug grab-bag PRs (#2329, #1723). None touch firstChild, CDATA, or multi-child-node text extraction.
Activity
VANDRANKI commentedon Sep 11, 2026
Closing this myself: this doesn't reproduce on current
main. PR #2432 (merged 2026-09-09, before I filed this issue on 2026-09-11) already rewrote_get_data_by_tag_name/_read_contentto walk all of an element's child nodes (TEXT_NODEandCDATA_SECTION_NODE) and concatenate them, instead of reading onlyfirstChild. I must have been looking at a stale copy of the file when I filed this. Sorry for the noise.