python/cpython · error · AssertionError
unexpected %r char in declaration
Error message
unexpected %r char in declaration
What it means
AssertionError raised by _markupbase.ParserBase._scan_decl when a declaration contains a character the scanner has no rule for — not a name char, not one of _decl_otherchars, not '['. It is the catch-all for malformed <!NAME ...> bodies, echoing the offending character.
Source
Thrown at Lib/_markupbase.py:134
j = m.end()
elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
name, j = self._scan_name(j, i)
elif c in self._decl_otherchars:
j = j + 1
elif c == "[":
# this could be handled in a separate doctype parser
if decltype == "doctype":
j = self._parse_doctype_subset(j + 1, i)
elif decltype in {"attlist", "linktype", "link", "element"}:
# must tolerate []'d groups in a content model in an element declaration
# also in data attribute specifications of attlist declaration
# also link type declaration subsets in linktype declarations
# also link attribute specification lists in link declarations
raise AssertionError("unsupported '[' char in %s declaration" % decltype)
else:
raise AssertionError("unexpected '[' char in declaration")
else:
raise AssertionError("unexpected %r char in declaration" % rawdata[j])
if j < 0:
return j
return -1 # incomplete
# Internal -- parse a marked section
# Override this to handle MS-word extension syntax <![if word]>content<![endif]>
def parse_marked_section(self, i, report=1):
rawdata= self.rawdata
assert rawdata[i:i+3] == '<![', "unexpected call to parse_marked_section()"
sectName, j = self._scan_name( i+3, i )
if j < 0:
return j
if sectName in {"temp", "cdata", "ignore", "include", "rcdata"}:
# look for standard ]]> ending
match= _markedsectionclose.search(rawdata, i+3)
elif sectName in {"if", "else", "endif"}:
# look for MS Office ]> ending
match= _msmarkedsectionclose.search(rawdata, i+3)View on GitHub (pinned to bc6749cc3b)
Solutions
- Feed the parser complete declarations (or use feed(data) followed by close() so buffering completes) instead of pre-splitting markup.
- Pre-filter input: remove or neutralize stray '<!' sequences that are not valid comments/doctype.
- Catch AssertionError, log the position via parser.getpos(), and skip past the next '>' before continuing.
- For untrusted HTML, run a repair pass (html5lib, bleach.cleaner) before stdlib parsing.
Example fix
# before
HTMLParser().feed("<!DOCTYPE html^>") # AssertionError: unexpected '^' char in declaration
# after
raw = re.sub(r'<!([\w-]+)[^>]*>', lambda m: m.group(0) if m.group(1).lower()=='doctype' else '', raw)
HTMLParser().feed(raw) Defensive patterns
Strategy: try-catch
Validate before calling
import re
_BAD_DECL_CHAR = re.compile(r'<!\s*[\w-]+[^>]*[^\w\s\[\]\|()\'\",#%-]>')
def decl_chars_ok(raw: str) -> bool:
return not _BAD_DECL_CHAR.search(raw) Try / catch
try:
parser.feed(chunk)
except AssertionError as e:
if 'char in declaration' in str(e):
chunk = re.sub(r'<![^>]*>', '', chunk) # drop the offending declaration
parser.reset(); parser.feed(chunk)
else:
raise Prevention
- Always call parser.close() after the last feed() so truncated declarations resolve or fail cleanly.
- Pre-filter stray '<!' sequences from template output.
- Log parser.getpos() on failure to pinpoint the malformed declaration.
When it happens
Trigger: HTMLParser.feed() with a declaration containing stray punctuation/control characters, e.g. <!DOCTYPE html!> or <!ATTLIST a b 'weird>; unterminated or truncated declarations at buffer end can also leave the cursor on an unexpected character.
Common situations: Parsing minified/mangled HTML, template fragments with leftover '<!' tokens, binary data fed as text, or feeding a document in chunks where a declaration is split and the tail begins mid-syntax.
Related errors
- unexpected '[' char in declaration
- expected name token at %r
- unsupported '[' char in %s declaration
- unknown status keyword %r in marked section
- unexpected char in internal subset (in %r)
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/06fae2e99da4e606.
Report an issue: GitHub.