astral-sh/ruff · error · ValueError
Brackets are not balanced for type {rust_type_str}
Error message
Brackets are not balanced for type {rust_type_str} What it means
generate.py is the maintainer code-generation script that regenerates the ruff_python_ast sources. extract_type_argument() takes a Rust field type such as Box<Expr> or Vec<Stmt> and returns the generic argument (stripping a crate:: prefix). It raises ValueError when a rule string contains '<' but the last '>' is missing or occurs at or before the '<' — i.e. the generic brackets of an AST field rule are unbalanced.
Source
Thrown at crates/ruff_python_ast/generate.py:227
"u32",
"bool",
"Number",
"IpyEscapeKind",
]
# Extracts the type argument from a Rust type used in AST field syntax.
# Box<str> -> str
# Box<Expr> -> Expr
# If the type does not have a type argument, it will return the string.
# Does not support nested types
def extract_type_argument(rust_type_str: str) -> str:
open_bracket_index = rust_type_str.find("<")
if open_bracket_index == -1:
return rust_type_str
close_bracket_index = rust_type_str.rfind(">")
if close_bracket_index == -1 or close_bracket_index <= open_bracket_index:
raise ValueError(f"Brackets are not balanced for type {rust_type_str}")
inner_type = rust_type_str[open_bracket_index + 1 : close_bracket_index].strip()
inner_type = inner_type.replace("crate::", "")
return inner_type
class SequenceKind(Enum):
VEC = "vec"
BOXED_SLICE = "boxed_slice"
THIN_VEC = "thin_vec"
def split_sequence_type(rule: str) -> tuple[SequenceKind | None, str]:
if "&" in rule:
raise ValueError(f"`&T*` is unsupported; use `Box<[T]>`: {rule}")
if "*" in rule:
if rule.endswith("*") and rule.count("*") == 1:
return SequenceKind.VEC, rule[:-1]View on GitHub (pinned to d1087a4b9e)
Solutions
- Open the AST definition the generator was consuming and balance the generic brackets (find the '<' that has no matching '>').
- Re-run python crates/ruff_python_ast/generate.py and review the regenerated diff.
- Run cargo check -p ruff_python_ast to confirm the regenerated code compiles.
Example fix
# before (AST field rule) decorator_list: Box<Expr # after decorator_list: Box<Expr>
Defensive patterns
Strategy: validation
Validate before calling
def generics_balanced(t: str) -> bool:
i, j = t.find('<'), t.rfind('>')
return i == -1 or (j != -1 and j > i)
# run over every field rule before invoking generate.py
assert all(generics_balanced(rule) for rule in field_rules) Prevention
- Run cargo dev generate-all locally before committing AST changes; it fails fast on malformed rules.
- Keep field types in the documented shapes: T, T?, T*, Vec<T>, ThinVec<T>, Box<[T]>, Box<T>.
- Review the generated diff in the PR so DSL typos surface immediately.
When it happens
Trigger: Running python crates/ruff_python_ast/generate.py (or cargo dev generate-all) after editing the AST definitions so a field type reads like 'Box<Expr', 'Vec<Stmt', or has the closing '>' before the opening '<'.
Common situations: Hand-editing AST node definitions and dropping a closing bracket; truncating a type during copy-paste from Rust source; renaming a type mid-refactor and losing a bracket.
Related errors
- `&T*` is unsupported; use `Box<[T]>`: {rule}
- `*` must be at the end: {rule}
- Unclosed collection type: {rule}
- `?` must be at the end: {rule}
- optional field cannot be sequence or slice: {self.rule}
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/80cf0bfa70f3bbbe.
Report an issue: GitHub.