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 ast

View on GitHub (pinned to 16e94cbf7f)

Solutions

  1. Add the missing `}` for the rule named in the message.
  2. Use brace-matching in your editor to locate the unclosed block.
  3. Format the file with a CSS formatter to surface the imbalance.
  4. 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

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


AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12). Data as JSON: /api/errors/656ebae411898b44. Report an issue: GitHub.