{"record":{"id":"61c9551007e4e941","repo":"tailwindlabs/tailwindcss","slug":"missing-closing-at-parent-name-parent-param","errorCode":null,"errorMessage":"Missing closing } at ${parent.name} ${parent.params}","messagePattern":"Missing closing \\} at (.+?) (.+?)","errorType":"exception","errorClass":"CssSyntaxError","httpStatus":null,"severity":"error","filePath":"packages/tailwindcss/src/css-parser.ts","lineNumber":582,"sourceCode":"      node.src = [source, bufferStart, input.length]\n      node.dst = [source, bufferStart, input.length]\n    }\n\n    ast.push(node)\n  }\n\n  // When we are done parsing then everything should be balanced. If we still\n  // have a leftover `parent`, then it means that we have an unterminated block.\n  if (closingBracketStack.length > 0 && parent) {\n    if (parent.kind === 'rule') {\n      throw new CssSyntaxError(\n        `Missing closing } at ${parent.selector}`,\n        parent.src ? [parent.src[0], parent.src[1], parent.src[1]] : null,\n      )\n    }\n\n    if (parent.kind === 'at-rule') {\n      throw new CssSyntaxError(\n        `Missing closing } at ${parent.name} ${parent.params}`,\n        parent.src ? [parent.src[0], parent.src[1], parent.src[1]] : null,\n      )\n    }\n  }\n\n  if (licenseComments.length > 0) {\n    return (licenseComments as AstNode[]).concat(ast)\n  }\n\n  return ast\n}\n\nexport function parseAtRule(buffer: string, nodes: AstNode[] = []): AtRule {\n  let name = buffer\n  let params = ''\n\n  // Assumption: The smallest at-rule in CSS right now is `@page`, this means","sourceCodeStart":564,"sourceCodeEnd":600,"githubUrl":"https://github.com/tailwindlabs/tailwindcss/blob/16e94cbf7f965c5ad697e90e940b5e178efad67c/packages/tailwindcss/src/css-parser.ts#L564-L600","documentation":"Sibling of error 55, thrown at end-of-input when the unclosed parent node is an at-rule (`@media`, `@layer`, `@supports`, etc.) instead of a style rule. The bracket stack is non-empty at EOF and `parent.kind === 'at-rule'`. The message embeds both the at-rule name and its params (e.g. `@media (min-width: 100px)`) and the source position comes from the at-rule's `src`.","triggerScenarios":"An `@media`, `@layer`, `@supports`, `@container`, or `@keyframes` block missing its closing `}` at EOF; nested at-rules where an inner block was left open; truncated file.","commonSituations":"Author forgot to close a media query; large files where a block closer was accidentally deleted; generated CSS missing the closer.","solutions":["Add the missing `}` for the at-rule named in the message.","Use brace-matching to locate the unclosed at-rule block.","Run a CSS formatter to identify the imbalance.","If generated, ensure the template always closes at-rule blocks."],"exampleFix":"/* before */\n@media (min-width: 100px) {\n  .x { color: red; }\n/* after */\n@media (min-width: 100px) {\n  .x { color: red; }\n}","handlingStrategy":"validation","validationCode":"// Reuse the brace-balance check; the message names the at-rule\nfunction bracesBalanced(css: string): boolean {\n  let depth = 0;\n  for (const ch of css) {\n    if (ch === '{') depth++;\n    else if (ch === '}') depth--;\n    if (depth < 0) return false;\n  }\n  return depth === 0;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const ast = CSS.parse(input);\n} catch (e) {\n  if (e instanceof CssSyntaxError && e.message.startsWith('Missing closing }')) {\n    // message includes `@media ...` — close that block\n  } else throw e;\n}","preventionTips":["Always close @media / @layer / @supports / @keyframes blocks.","Use brace matching; format CSS regularly.","For generated CSS, test that every at-rule emits a closer."],"tags":["tailwind-v4","css-parser","unclosed-block","at-rule","syntax-error"],"backgroundTag":null,"analyzedSha":"16e94cbf7f965c5ad697e90e940b5e178efad67c","analyzedAt":"2026-08-12T06:02:42.469Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}