{"record":{"id":"5159932fce4ece16","repo":"twigphp/Twig","slug":"unclosed-s","errorCode":null,"errorMessage":"Unclosed \"%s\".","messagePattern":"Unclosed \"(.+?)\"\\.","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/Lexer.php","lineNumber":260,"sourceCode":"                case self::STATE_VAR:\n                    $this->lexVar();\n                    break;\n\n                case self::STATE_STRING:\n                    $this->lexString();\n                    break;\n\n                case self::STATE_INTERPOLATION:\n                    $this->lexInterpolation();\n                    break;\n            }\n        }\n\n        $this->pushToken(Token::EOF_TYPE);\n\n        if ($this->brackets) {\n            [$expect, $lineno] = array_pop($this->brackets);\n            throw new SyntaxError(\\sprintf('Unclosed \"%s\".', $expect), $lineno, $this->source);\n        }\n\n        return new TokenStream($this->tokens, $this->source);\n    }\n\n    private function lexData(): void\n    {\n        // if no matches are left we return the rest of the template as simple text token\n        if ($this->position == \\count($this->positions[0]) - 1) {\n            $text = substr($this->code, $this->cursor);\n            $this->pushToken(Token::TEXT_TYPE, $this->normalizeNewlines($text));\n            $this->moveCursor($text);\n\n            return;\n        }\n\n        // Find the first token after the current cursor\n        $position = $this->positions[0][++$this->position];","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/Lexer.php#L242-L278","documentation":"At the end of Lexer::tokenize, after the EOF token is pushed, Twig checks the bracket stack (`$this->brackets`). Any still-open delimiter — `{%`, `{{`, or `(` — means the template ended before the construct was closed, so Twig throws this SyntaxError with the line number where the construct was opened. This is a compile-time (lexing) error caught before any rendering occurs.","triggerScenarios":"A template ending with `{% if x %}` without `{% endif %}`... more precisely an unclosed `{%`/`{{` (e.g. `{% block x %}` opened via `{%` but the tag never closed with `%}`), or an unclosed `(` inside an expression reaching EOF. The stack records the expected closer and its opening line, producing 'Unclosed \"(\".', etc.","commonSituations":"Truncated template files (partial writes, bad deploys); editing errors that delete a closing `%}` or `}}`; templates built by string concatenation where a piece got dropped; long `{% verbatim %}`/comment blocks accidentally swallowing a closer.","solutions":["Open the template at the line number given in the SyntaxError and add the missing closing delimiter (`%}`, `}}`, or `)`).","Run `php bin/console lint:twig <dir>` (Symfony) or Twig's lint command to find all unclosed constructs across a template tree.","Verify file integrity after deploys (checksum/size) to catch truncated template uploads.","If templates are generated dynamically, validate each generated template with `$twig->parse($twig->tokenize(new \\Twig\\Source($code, $name)))` before writing it."],"exampleFix":"// before (Twig)\n{% if user.active %}\n  Hello {{ user.name\n\n// after\n{% if user.active %}\n  Hello {{ user.name }}\n{% endif %}","handlingStrategy":"try-catch","validationCode":"// Validate template syntax before storing/deploying\ntry {\n    $twig->parse($twig->tokenize(new \\Twig\\Source($code, $name)));\n} catch (\\Twig\\Error\\SyntaxError $e) {\n    // reject template: 'Unclosed ...' at line N\n}","typeGuard":null,"tryCatchPattern":"try {\n    $html = $twig->render('page.html.twig', $context);\n} catch (\\Twig\\Error\\SyntaxError $e) {\n    if (str_contains($e->getMessage(), 'Unclosed \"')) {\n        // surface template name + line from $e->getSourceContext() / getTemplateLine()\n    } else { throw $e; }\n}","preventionTips":["Run twig lint (lint:twig) on every template before deploy","Verify template file sizes/checksums after uploads to catch truncated files","Never assemble templates by naive string concatenation; use includes/partials","Pair every `{%`/`{{` opener with its closer before saving; editors with Twig syntax highlighting help"],"tags":["twig","lexer","syntax-error","unclosed-bracket"],"backgroundTag":"unclosed-bracket","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}