{"record":{"id":"db41e1c0496ff465","repo":"python/cpython","slug":"unexpected-char-r-in-internal-subset","errorCode":null,"errorMessage":"unexpected char %r in internal subset","messagePattern":"unexpected char %r in internal subset","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"Lib/_markupbase.py","lineNumber":244,"sourceCode":"                    return j\n                if rawdata[j] == \";\":\n                    j = j + 1\n            elif c == \"]\":\n                j = j + 1\n                while j < n and rawdata[j].isspace():\n                    j = j + 1\n                if j < n:\n                    if rawdata[j] == \">\":\n                        return j\n                    self.updatepos(declstartpos, j)\n                    raise AssertionError(\"unexpected char after internal subset\")\n                else:\n                    return -1\n            elif c.isspace():\n                j = j + 1\n            else:\n                self.updatepos(declstartpos, j)\n                raise AssertionError(\"unexpected char %r in internal subset\" % c)\n        # end of buffer reached\n        return -1\n\n    # Internal -- scan past <!ELEMENT declarations\n    def _parse_doctype_element(self, i, declstartpos):\n        name, j = self._scan_name(i, declstartpos)\n        if j == -1:\n            return -1\n        # style content model; just skip until '>'\n        rawdata = self.rawdata\n        if '>' in rawdata[j:]:\n            return rawdata.find(\">\", j) + 1\n        return -1\n\n    # Internal -- scan past <!ATTLIST declarations\n    def _parse_doctype_attlist(self, i, declstartpos):\n        rawdata = self.rawdata\n        name, j = self._scan_name(i, declstartpos)","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_markupbase.py#L226-L262","documentation":"AssertionError raised by _markupbase.ParserBase._parse_doctype_subset when a character inside the DOCTYPE internal subset is not '<', '%', ']', or whitespace. It is the subset scanner's catch-all for stray content (text, quotes, operators) that is illegal between declarations, echoing the offending character.","triggerScenarios":"HTMLParser.feed() on <!DOCTYPE d [ stray text ]>, <!DOCTYPE d [ \"unterminated ]>, or any subset containing free text or stray punctuation outside of a proper <!...> declaration, %-reference, or the closing ']'.","commonSituations":"Documents with prose accidentally inside the doctype (bad template slots); quote-unbalanced entity values swallowing the ']'; feeding binary/mojibake data; hand-crafted test fixtures with sloppy subsets.","solutions":["Keep the internal subset to declarations only; move text outside the DOCTYPE.","Balance and terminate entity values (<!ENTITY e \"v\">) so quotes do not leak past ']'.","Catch AssertionError, use getpos() to locate the character, and repair or strip the subset (re.sub(r'\\[[^\\]]*\\]', '', doctype)).","Drop the subset entirely when it is not semantically needed: feed '<!DOCTYPE root>' instead."],"exampleFix":"# before\nHTMLParser().feed('<!DOCTYPE d [ version 1.0 ]>')  # AssertionError: unexpected char\n\n# after\nHTMLParser().feed('<!DOCTYPE d>')\n# or keep only legal content: '<!DOCTYPE d [ <!ENTITY ver \"1.0\"> ]>'","handlingStrategy":"try-catch","validationCode":"import re\n\ndef subset_content_ok(raw: str) -> bool:\n    m = re.search(r'<!DOCTYPE[^\\[]*\\[([^\\]]*)\\]', raw, re.I | re.S)\n    if not m:\n        return True\n    body = re.sub(r'<!--.*?-->|<!\\w+[^>]*>|%[^;]*;|\\s+', '', m.group(1), flags=re.S)\n    return body == ''  # nothing but decls, comments, PE refs, whitespace","typeGuard":null,"tryCatchPattern":"try:\n    parser.feed(data)\nexcept AssertionError as e:\n    if 'in internal subset' in str(e):\n        data = re.sub(r'(<!DOCTYPE[^\\[]*)\\[.*?\\]', r'\\1', data, flags=re.S | re.I)\n        parser.reset(); parser.feed(data)\n    else:\n        raise","preventionTips":["Keep subsets free of free text; balance all quotes in entity values.","If the subset is cosmetic, remove it before parsing.","Feed input as text with a known encoding to avoid stray bytes inside subsets."],"tags":["markup","html-parser","doctype","parsing"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}