python/cpython · error · AssertionError

unexpected char after internal subset

Error message

unexpected char after internal subset

What it means

AssertionError raised by _markupbase.ParserBase._parse_doctype_subset after the closing ']' of a DOCTYPE internal subset: whitespace is skipped and the next character must be '>'. Any other character at that position means the DOCTYPE declaration is malformed after its subset, and the scanner aborts.

Source

Thrown at Lib/_markupbase.py:237

            elif c == "%":
                # parameter entity reference
                if (j + 1) == n:
                    # end of buffer; incomplete
                    return -1
                s, j = self._scan_name(j + 1, declstartpos)
                if j < 0:
                    return j
                if rawdata[j] == ";":
                    j = j + 1
            elif c == "]":
                j = j + 1
                while j < n and rawdata[j].isspace():
                    j = j + 1
                if j < n:
                    if rawdata[j] == ">":
                        return j
                    self.updatepos(declstartpos, j)
                    raise AssertionError("unexpected char after internal subset")
                else:
                    return -1
            elif c.isspace():
                j = j + 1
            else:
                self.updatepos(declstartpos, j)
                raise AssertionError("unexpected char %r in internal subset" % c)
        # end of buffer reached
        return -1

    # Internal -- scan past <!ELEMENT declarations
    def _parse_doctype_element(self, i, declstartpos):
        name, j = self._scan_name(i, declstartpos)
        if j == -1:
            return -1
        # style content model; just skip until '>'
        rawdata = self.rawdata
        if '>' in rawdata[j:]:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Ensure the DOCTYPE ends '] >' — i.e. the '>' immediately after optional whitespace following ']'.
  2. If mangled input is unavoidable, catch AssertionError and skip forward to the next '>' before resuming feed().
  3. Sanitize with a regex that forces well-formedness: re.sub(r'\]\s*[^>\s][^>]*>', ']>', raw).
  4. Feed complete declarations per chunk boundary rather than splitting mid-doctype.

Example fix

# before
HTMLParser().feed('<!DOCTYPE d [ <!ENTITY e "x"> ] junk>')  # AssertionError after subset

# after
HTMLParser().feed('<!DOCTYPE d [ <!ENTITY e "x"> ]>')
Defensive patterns

Strategy: try-catch

Validate before calling

import re

def doctest_end_ok(raw: str) -> bool:
    return not re.search(r'\]\s*[^>\s][^>]*>', raw)

Try / catch

try:
    parser.feed(data)
except AssertionError as e:
    if 'after internal subset' in str(e):
        data = re.sub(r'\]\s*[^>]*>', ']>', data)
        parser.reset(); parser.feed(data)
    else:
        raise

Prevention

When it happens

Trigger: HTMLParser.feed() on <!DOCTYPE d [ ... ] x> — junk between ']' and '>'; also <!DOCTYPE d [ ... ]<?xml ...?> or a truncated buffer where a non-'>' char follows the bracket.

Common situations: Template concatenation placing content right after the subset; regex-based HTML edits that drop the final '>'; chunked streaming where the next chunk's text is glued after ']' without '>' first.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/1fbfe3f5604051fc. Report an issue: GitHub.