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
- Move constructs like <![...[ ]]> sections out of the internal subset or remove them.
- Correct the declaration keyword to ELEMENT/ATTLIST/ENTITY/NOTATION as intended.
- Strip the whole internal subset (regex the '[...]' portion of the DOCTYPE) if it is not needed for parsing.
- 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
- Restrict internal subsets to ELEMENT/ATTLIST/ENTITY/NOTATION.
- Never nest marked sections inside a DOCTYPE subset.
- Strip or validate DTD snippets coming from third-party feeds.
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
- unexpected char in internal subset (in %r)
- unsupported '[' char in %s declaration
- unexpected char after internal subset
- unexpected char %r in internal subset
- unexpected '[' char in declaration
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/9367b126d4cc35ce.
Report an issue: GitHub.