{"record":{"id":"d356ef34db8bd77a","repo":"Textualize/textual","slug":"unexpected-end-of-file-did-you-forget-a-u-d356ef","errorCode":null,"errorMessage":"Unexpected end of file; did you forget a '}' ? / Unexpected end of markup","messagePattern":"Unexpected end of file; did you forget a '\\}' \\? / Unexpected end of markup","errorType":"exception","errorClass":"UnexpectedEnd","httpStatus":null,"severity":"error","filePath":"src/textual/css/tokenizer.py","lineNumber":365,"sourceCode":"\n    def skip_to(self, expect: Expect) -> Token:\n        \"\"\"Skip tokens.\n\n        Args:\n            expect: Expect object describing the expected token.\n\n        Raises:\n            UnexpectedEndOfText: If end of file is reached.\n\n        Returns:\n            A new token.\n        \"\"\"\n        line_no = self.line_no\n        col_no = self.col_no\n\n        while True:\n            if line_no >= len(self.lines):\n                raise UnexpectedEnd(\n                    self.read_from,\n                    self.code,\n                    (line_no, col_no),\n                    (\n                        \"Unexpected end of file; did you forget a '}' ?\"\n                        if expect._expect_semicolon\n                        else \"Unexpected end of markup\"\n                    ),\n                )\n            line = self.lines[line_no]\n            match = expect.search(line, col_no)\n\n            if match is None:\n                line_no += 1\n                col_no = 0\n            else:\n                self.line_no = line_no\n                self.col_no = match.span(0)[0]","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/css/tokenizer.py#L347-L383","documentation":"UnexpectedEnd raised by Tokenizer.skip_to when scanning forward (e.g. to the end of a block or declaration list) hits the end of input. Message hints at a missing '}' when a semicolon was expected, else 'Unexpected end of markup'.","triggerScenarios":"skip_to(',') or skip_to(';') on input where the terminator never appears — typically an unclosed rule block or a declaration list that runs to EOF without its closing brace/semicolon.","commonSituations":"Same class of issues as [106]: truncated or hand-written CSS missing the closing '}' — but surfacing through the skip-to recovery path rather than get_token's main loop.","solutions":["Close every '{' with a matching '}'","Ensure declaration lists are terminated","Validate CSS strings before passing them to Stylesheet by counting braces"],"exampleFix":"/* before */\nScreen { layout: vertical;\n/* after */\nScreen { layout: vertical; }","handlingStrategy":"validation","validationCode":"def css_terminators_ok(css: str) -> bool:\n    css = css.strip()\n    return css.endswith('}') and '{' not in css.split('}')[-1] and braces_balanced(css)","typeGuard":null,"tryCatchPattern":"from textual.css.tokenizer import UnexpectedEnd\ntry:\n    stylesheet.parse()\nexcept UnexpectedEnd as e:\n    report(f\"missing '}}' near {e.location}: {e}\")","preventionTips":["Close every block with '}'","Terminate declaration lists","Brace-count generated CSS"],"tags":["textual","css","tokenizer","unexpected-end"],"backgroundTag":"unexpected-end-of-input","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}