tailwindlabs/tailwindcss · error · Error
The pattern `${pattern}` is not balanced.
Error message
The pattern `${pattern}` is not balanced. What it means
Thrown by expand() in brace-expansion.ts when a '{' character has no matching closing '}'. The parser walks the pattern tracking nesting depth; if it never returns to depth 0, endIndex stays -1 and the pattern is rejected as unbalanced. This mirrors bash brace-expansion semantics where every opened brace must close.
Source
Thrown at packages/tailwindcss/src/utils/brace-expansion.ts:30
// Find the matching closing brace
let depth = 0
let endIndex = rest.lastIndexOf('}')
for (let i = 0; i < rest.length; i++) {
let char = rest[i]
if (char === '{') {
depth++
} else if (char === '}') {
depth--
if (depth === 0) {
endIndex = i
break
}
}
}
if (endIndex === -1) {
throw new Error(`The pattern \`${pattern}\` is not balanced.`)
}
let inside = rest.slice(1, endIndex)
let post = rest.slice(endIndex + 1)
let parts: string[]
if (isSequence(inside)) {
parts = expandSequence(inside)
} else {
parts = segment(inside, ',')
}
parts = parts.flatMap((part) => expand(part))
let expandedTail = expand(post)
for (let tail of expandedTail) {
for (let part of parts) {View on GitHub (pinned to 16e94cbf7f)
Solutions
- Balance the braces: add the missing '}' (e.g. 'img-{icon' -> 'img-{icon}').
- If a literal '{' is intended, escape or avoid the expansion utility for that input.
- Validate the pattern before calling expand(): count '{' and '}' and ensure they match.
Example fix
// before — throws
let parts = expand('page-{1..5')
// after
let parts = expand('page-{1..5}') Defensive patterns
Strategy: validation
Validate before calling
function isBalanced(pattern: string): boolean {
let depth = 0
for (const ch of pattern) {
if (ch === '{') depth++
else if (ch === '}') depth--
if (depth < 0) return false
}
return depth === 0
}
if (!isBalanced(pattern)) {
throw new Error(`Unbalanced braces in: ${pattern}`)
}
expand(pattern) Try / catch
try {
const parts = expand(pattern)
} catch (e) {
if (e instanceof Error && /not balanced/.test(e.message)) {
// fall back to literal pattern
return [pattern]
}
throw e
} Prevention
- Sanitize user/dynamic input before passing to brace expansion.
- Count braces when building patterns from templates.
- Wrap expand() calls in try/catch with a literal fallback for untrusted input.
When it happens
Trigger: Calling expand() with a pattern containing an unmatched '{', e.g. 'img-{icon' or 'a{b,c'. Internally this is used by Tailwind's arbitrary value and variant parsing, so malformed arbitrary values like 'bg-{red' in class names can surface here.
Common situations: Typos in utility class names with brace syntax. Dynamically generated class strings where a template literal leaves a dangling brace. User input fed into a brace-expansion-based path/glob utility without sanitization.
Related errors
- Step cannot be zero in sequence expansion.
- The prefix "${resolvedConfig.prefix}" is invalid. Prefixes m
- `addVariant('${name}')` defines an invalid variant name. Var
- `matchUtilities({ '${name}' : … })` defines an invalid utili
- `@utility ${node.params}` is empty. Utilities should include
AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12).
Data as JSON: /api/errors/9eeac38551f243b6.
Report an issue: GitHub.