fishaudio/fish-speech · error · UnbalancedBracesError
Unbalanced braces: '%s' % pattern
Error message
Unbalanced braces: '%s' % pattern
What it means
braceexpand's parse_pattern raises UnbalancedBracesError when an opening '{' has no matching closing brace in the pattern. The expansion cannot be computed deterministically so it fails fast.
Source
Thrown at fish_speech/utils/braceexpand.py:136
# print 'literal:', pattern[start:pos]
items.append([pattern[start:pos]])
start = pos
bracketdepth += 1
elif pattern[pos] == "}":
bracketdepth -= 1
if bracketdepth == 0:
# print 'expression:', pattern[start+1:pos]
expr = pattern[start + 1 : pos]
item = parse_expression(expr, escape)
if item is None: # not a range or sequence
items.extend([["{"], parse_pattern(expr, escape), ["}"]])
else:
items.append(item)
start = pos + 1 # skip the closing brace
pos += 1
if bracketdepth != 0: # unbalanced braces
raise UnbalancedBracesError("Unbalanced braces: '%s'" % pattern)
if start < pos:
items.append([pattern[start:]])
return ("".join(item) for item in product(*items))
def parse_expression(expr: str, escape: bool) -> Optional[Iterable[str]]:
int_range_match = int_range_re.match(expr)
if int_range_match:
return make_int_range(*int_range_match.groups())
char_range_match = char_range_re.match(expr)
if char_range_match:
return make_char_range(*char_range_match.groups())
return parse_sequence(expr, escape)
View on GitHub (pinned to befe400174)
Solutions
- Balance the braces or escape literal braces with backslash: '\{'
- Use escape=True when literal braces should not be expanded
- Catch UnbalancedBracesError and surface a clear message to the end user if patterns are user input
Example fix
# before
braceexpand("file{1,2.txt")
# after
braceexpand("file\\{1,2\\}.txt") # or "file{1,2}.txt" Defensive patterns
Strategy: try-catch
Validate before calling
def balanced(s):
d = 0
for ch in s:
if ch == "{": d += 1
elif ch == "}": d -= 1
if d < 0: return False
return d == 0
assert balanced(pattern) Try / catch
from fish_speech.utils.braceexpand import UnbalancedBracesError
try:
list(braceexpand(pattern))
except UnbalancedBracesError:
pattern = pattern.replace("{", "\\{").replace("}", "\\}") Prevention
- Validate user patterns before expanding
- Prefer escape=True for literal braces
When it happens
Trigger: Calling braceexpand()/parse_pattern() with strings like 'a{1,2' or '{foo' where a brace opens but never closes.
Common situations: User-supplied glob/brace patterns from CLI args or config; escaping backslashes consumed by an extra shell/JSON layer removing a literal closing brace.
Related errors
AI-assisted analysis of fishaudio/fish-speech@befe400174 (2026-08-27).
Data as JSON: /api/errors/4b063da85b1700e4.
Report an issue: GitHub.