rust-lang/rust · error · RuntimeError

Cannot parse an HTML file {!r}: {}

Error message

Cannot parse an HTML file {!r}: {}

What it means

Raised as RuntimeError by CachedFiles.get_tree() when ET.fromstringlist() (using the CustomHTMLParser) fails to parse the file as HTML/XML. The custom parser is lenient about void elements and empty attributes but will still throw on malformed markup; the original parse exception is embedded in the message. Note the code suppresses the chain (FIXME: py2, should use 'raise ... from').

Source

Thrown at src/etc/htmldocck.py:280

        with io.open(abspath, encoding="utf-8") as f:
            data = f.read()
            self.files[path] = data
            return data

    def get_tree(self, path):
        path = self.resolve_path(path)
        if path in self.trees:
            return self.trees[path]

        abspath = self.get_absolute_path(path)
        if not (os.path.exists(abspath) and os.path.isfile(abspath)):
            raise FailedCheck("File does not exist {!r}".format(path))

        with io.open(abspath, encoding="utf-8") as f:
            try:
                tree = ET.fromstringlist(f.readlines(), CustomHTMLParser())
            except Exception as e:
                raise RuntimeError(  # noqa: B904 FIXME: py2
                    "Cannot parse an HTML file {!r}: {}".format(path, e)
                )
            self.trees[path] = tree
            return self.trees[path]

    def get_dir(self, path):
        path = self.resolve_path(path)
        abspath = self.get_absolute_path(path)
        if not (os.path.exists(abspath) and os.path.isdir(abspath)):
            raise FailedCheck("Directory does not exist {!r}".format(path))


def check_string(data, pat, regexp):
    pat = pat.replace("{{channel}}", channel)
    if not pat:
        return True  # special case a presence testing
    elif regexp:
        return re.search(pat, data, flags=re.UNICODE) is not None

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. Open the referenced file and locate the malformed markup around the parser's reported offset.
  2. If the HTML is genuinely broken, file a rustdoc bug and xfail/skip the test temporarily.
  3. Ensure the file is actually HTML (not a 404 page or redirect) and is valid UTF-8.
  4. If an entity is the culprit, confirm it is a named HTML entity the parser knows.

Example fix

// before - rustdoc emitted an unbalanced tag
<div class='item'><span>foo</div>

// after - fix the source HTML so tags are properly closed
<div class='item'><span>foo</span></div>
Defensive patterns

Strategy: try-catch

Validate before calling

from pathlib import Path
from xml.etree import ElementTree as ET

def html_parses_cleanly(abspath) -> bool:
    try:
        with open(abspath, encoding="utf-8") as f:
            ET.fromstringlist(f.readlines(), CustomHTMLParser())
        return True
    except Exception as e:
        return False

if not html_parses_cleanly(os.path.join(doc_root, path)):
    raise SystemExit(f"htmldocck cannot parse {path!r} as HTML; inspect the file")

Type guard

null

Try / catch

try:
    tree = cache.get_tree(path)
except RuntimeError as e:
    if "Cannot parse an HTML file" in str(e):
        logging.error("rustdoc produced unparseable HTML for %s; file a rustdoc bug", path)
    raise

Prevention

When it happens

Trigger: A directive triggers get_tree() on a file that exists but whose content the parser cannot turn into an ElementTree: unclosed tags that aren't void elements, encoding issues, content that is not HTML at all, or an HTMLParser error during entity/charref handling.

Common situations: rustdoc regressed and emitted invalid HTML; the file is actually a redirect/placeholder (not real HTML); an entity reference in the document is not in name2codepoint; encoding mismatch (file declared utf-8 but contains invalid bytes).

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/e6c1c23a0843eddc. Report an issue: GitHub.