python/cpython · error · AssertionError

unknown declaration %r in internal subset

Error message

unknown declaration %r in internal subset

What it means

AssertionError raised by _markupbase.ParserBase._parse_doctype_subset when a markup declaration inside a DOCTYPE internal subset has a name other than attlist, element, entity, or notation. XML/SHTML restrict internal-subset declarations to those four (plus comments and PE references); the stdlib scanner enforces this and rejects anything else, echoing the scanned name.

Source

Thrown at Lib/_markupbase.py:211

                        "unexpected char in internal subset (in %r)" % s
                    )
                if (j + 2) == n:
                    # end of buffer; incomplete
                    return -1
                if (j + 4) > n:
                    # end of buffer; incomplete
                    return -1
                if rawdata[j:j+4] == "<!--":
                    j = self.parse_comment(j, report=0)
                    if j < 0:
                        return j
                    continue
                name, j = self._scan_name(j + 2, declstartpos)
                if j == -1:
                    return -1
                if name not in {"attlist", "element", "entity", "notation"}:
                    self.updatepos(declstartpos, j + 2)
                    raise AssertionError(
                        "unknown declaration %r in internal subset" % name
                    )
                # handle the individual names
                meth = getattr(self, "_parse_doctype_" + name)
                j = meth(j, declstartpos)
                if j < 0:
                    return j
            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 == "]":

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Move constructs like <![...[ ]]> sections out of the internal subset or remove them.
  2. Correct the declaration keyword to ELEMENT/ATTLIST/ENTITY/NOTATION as intended.
  3. Strip the whole internal subset (regex the '[...]' portion of the DOCTYPE) if it is not needed for parsing.
  4. Catch AssertionError and skip to the matching '>' of the offending declaration, then continue feed().

Example fix

# before
HTMLParser().feed('<!DOCTYPE d [ <!ELEMENT2 a EMPTY > ]>')  # AssertionError: unknown declaration

# after
HTMLParser().feed('<!DOCTYPE d [ <!ELEMENT a EMPTY > ]>')
# or: HTMLParser().feed(re.sub(r'<!DOCTYPE\s+\w+\s*\[.*?\]', '<!DOCTYPE d>', raw, flags=re.S))
Defensive patterns

Strategy: try-catch

Validate before calling

import re
_OK_NAMES = {'attlist', 'element', 'entity', 'notation'}

def subset_decls_ok(raw: str) -> bool:
    m = re.search(r'<!DOCTYPE[^\[]*\[([^\]]*)\]', raw, re.I | re.S)
    if not m:
        return True
    return all((n or '').lower() in _OK_NAMES
               for n in re.findall(r'<!(\w+)', m.group(1)))

Try / catch

try:
    parser.feed(data)
except AssertionError as e:
    if 'unknown declaration' in str(e):
        pos = data.find('<!', data.lower().find('<!doctype'))
        end = data.find('>', pos)
        data = data[:pos] + data[end + 1:]
        parser.reset(); parser.feed(data)
    else:
        raise

Prevention

When it happens

Trigger: HTMLParser.feed() on <!DOCTYPE d [ <!INCLUDE[ ... ]> ]> or <!DOCTYPE d [ <!ATTLIST2 ...> ]> — any '<!name' in the subset whose name is not in {attlist, element, entity, notation} (note: names are lowercased by _scan_name).

Common situations: Nested marked sections or conditional blocks misplaced inside the DOCTYPE subset; typos in DTD keywords; copying SGML constructs (e.g. <!SHORTREF>) into HTML doctypes; generated documents gluing fragments together.

Related errors


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