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

  1. Feed the parser complete declarations (or use feed(data) followed by close() so buffering completes) instead of pre-splitting markup.
  2. Pre-filter input: remove or neutralize stray '<!' sequences that are not valid comments/doctype.
  3. Catch AssertionError, log the position via parser.getpos(), and skip past the next '>' before continuing.
  4. 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

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


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