python/cpython · error · AssertionError

unsupported '[' char in %s declaration

Error message

unsupported '[' char in %s declaration

What it means

AssertionError raised by _markupbase.ParserBase._scan_decl when a '[' character appears inside an <!ATTLIST, <!LINKTYPE, <!LINK, or <!ELEMENT declaration. Bracketed groups are legal in those declarations' content models, but the stdlib's minimal declaration scanner does not implement them — only <!DOCTYPE [...]> internal subsets are handled — so it aborts.

Source

Thrown at Lib/_markupbase.py:130

            if c in "\"'":
                m = _declstringlit_match(rawdata, j)
                if not m:
                    return -1 # incomplete
                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

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Strip or skip <!...> declaration blocks before feeding data to HTMLParser (they carry no document content for HTML purposes).
  2. Catch AssertionError around feed() and skip/repair the offending declaration chunk, then resume feeding after the next '>'.
  3. Use a real XML/SGML tool (xml.sax, lxml) for documents that genuinely need DTD internal subsets.
  4. Override _scan_decl in a subclass to tolerate bracketed content models by scanning to the matching bracket first.

Example fix

# before
from html.parser import HTMLParser
HTMLParser().feed('<!ELEMENT doc (title) [ #PCDATA ]>')  # AssertionError

# after
import re
from html.parser import HTMLParser

clean = re.sub(r'<!\[?[^>]*\]?>', '', raw_markup)  # drop declarations
HTMLParser().feed(clean)
Defensive patterns

Strategy: try-catch

Validate before calling

import re

def decl_is_safe(chunk: str) -> bool:
    """Reject declarations containing '[' outside a doctype."""
    m = re.match(r'<!\s*(\w+)', chunk)
    if not m:
        return True
    return m.group(1).lower() == 'doctype' or '[' not in chunk

Try / catch

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

Prevention

When it happens

Trigger: html.parser.HTMLParser (or any ParserBase subclass) feeding markup like <!ELEMENT foo (a | b)* [ hidden ]> or <!ATTLIST x [ ... ]>. The scanner hits '[' with decltype already set to element/attlist/linktype/link and raises.

Common situations: Parsing DTD-bearing documents (older SGML-flavored HTML, EPUB/DocBook fragments, scraped legacy pages with inline declarations); test fixtures containing raw DTD text; feeding a full .dtd file into HTMLParser by mistake.

Related errors


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