tailwindlabs/tailwindcss · error · CssSyntaxError
Missing closing } at ${parent.selector}
Error message
Missing closing } at ${parent.selector} What it means
Thrown at end-of-input when the bracket stack is still non-empty (`closingBracketStack.length > 0`) and the unclosed parent node is a style `rule`. The parser reached the end of the file with at least one `{` that was never matched by a `}`. The error message embeds the rule's selector and the source position is derived from the rule's own `src` so users can jump to where the block opened.
Source
Thrown at packages/tailwindcss/src/css-parser.ts:575
// means that we have an at-rule that is not terminated with a semicolon at
// the end of the input.
if (buffer.charCodeAt(0) === AT_SIGN) {
let node = parseAtRule(buffer)
// Track the source location for source maps
if (source) {
node.src = [source, bufferStart, input.length]
node.dst = [source, bufferStart, input.length]
}
ast.push(node)
}
// When we are done parsing then everything should be balanced. If we still
// have a leftover `parent`, then it means that we have an unterminated block.
if (closingBracketStack.length > 0 && parent) {
if (parent.kind === 'rule') {
throw new CssSyntaxError(
`Missing closing } at ${parent.selector}`,
parent.src ? [parent.src[0], parent.src[1], parent.src[1]] : null,
)
}
if (parent.kind === 'at-rule') {
throw new CssSyntaxError(
`Missing closing } at ${parent.name} ${parent.params}`,
parent.src ? [parent.src[0], parent.src[1], parent.src[1]] : null,
)
}
}
if (licenseComments.length > 0) {
return (licenseComments as AstNode[]).concat(ast)
}
return astView on GitHub (pinned to 16e94cbf7f)
Solutions
- Add the missing `}` for the rule named in the message.
- Use brace-matching in your editor to locate the unclosed block.
- Format the file with a CSS formatter to surface the imbalance.
- If generated, fix the template to always emit a closing brace.
Example fix
/* before */
.x {
color: red;
/* after */
.x {
color: red;
} Defensive patterns
Strategy: validation
Validate before calling
function findUnclosedBlocks(css: string): boolean {
let depth = 0;
for (const ch of css) {
if (ch === '{') depth++;
else if (ch === '}') depth--;
}
return depth !== 0; // true => unclosed blocks remain
} Try / catch
try {
const ast = CSS.parse(input);
} catch (e) {
if (e instanceof CssSyntaxError && e.message.startsWith('Missing closing }')) {
// message names the rule selector; add its closing brace
} else throw e;
} Prevention
- Always close every rule block with }.
- Use brace matching and a CSS formatter.
- For large files, fold blocks to verify closure.
When it happens
Trigger: A rule missing its closing `}`, e.g. `.x { color: red;` at EOF; nested rules where an inner block was never closed; a truncated stylesheet. The branch keys on `parent.kind === 'rule'`.
Common situations: Author forgot the final `}`; file truncated during save/transfer; broken generated CSS; editor auto-close disabled.
Related errors
- Missing closing } at ${parent.name} ${parent.params}
- Invalid custom property, expected a value
- Invalid declaration: `${buffer.trim()}`
- Missing opening {
- Missing opening (
AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12).
Data as JSON: /api/errors/656ebae411898b44.
Report an issue: GitHub.