astral-sh/ruff · error · ValueError
`?` must be at the end: {rule}
Error message
`?` must be at the end: {rule} What it means
A trailing '?' marks an optional field (generated as Option<T>). FieldType.__init__ accepts exactly one '?' and only at the end of the rule; shapes like '?Expr', 'Expr??', or 'Vec<X>?' with the marker misplaced raise this ValueError.
Source
Thrown at crates/ruff_python_ast/generate.py:274
return sequence_kind, rule[len(prefix) : -len(suffix)]
return None, rule
@dataclass
class FieldType:
rule: str
name: str
inner: str
sequence_kind: SequenceKind | None = None
optional: bool = False
def __init__(self, rule: str) -> None:
self.rule = rule
self.optional = False
if "?" in rule:
if not rule.endswith("?") or rule.count("?") != 1:
raise ValueError(f"`?` must be at the end: {rule}")
self.optional = True
rule = rule[:-1]
self.sequence_kind, self.name = split_sequence_type(rule)
if self.optional and self.sequence_kind is not None:
raise ValueError(f"optional field cannot be sequence or slice: {self.rule}")
if self.sequence_kind is not None and (
not self.name or any(ch in self.name for ch in "?*&[]<>")
):
raise ValueError(f"Invalid collection element type: {rule}")
self.inner = extract_type_argument(self.name)
# ------------------------------------------------------------------------------
# Preamble
View on GitHub (pinned to d1087a4b9e)
Solutions
- Place a single '?' at the very end: 'Expr?'.
- Remove any duplicate '?' characters.
- Re-run the generator to confirm parsing succeeds.
Example fix
# before value: ?Expr # after value: Expr?
Defensive patterns
Strategy: validation
Validate before calling
def rule_ok(rule: str) -> bool:
return '?' not in rule or (rule.endswith('?') and rule.count('?') == 1) Prevention
- Optional marker '?' is always a single trailing character.
- Copy the shapes of existing optional fields when adding new ones.
When it happens
Trigger: Adding an optional field where the '?' is not the last character or appears more than once, then running the AST generator.
Common situations: Copy-paste typos; prefix-style optional markers borrowed from regex-like DSLs; editing rules in a hurry without the shapes cheatsheet.
Related errors
- optional field cannot be sequence or slice: {self.rule}
- Brackets are not balanced for type {rust_type_str}
- `&T*` is unsupported; use `Box<[T]>`: {rule}
- `*` must be at the end: {rule}
- Unclosed collection type: {rule}
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/51d5e7452231cf5a.
Report an issue: GitHub.