astral-sh/ruff · error · ValueError
optional field cannot be sequence or slice: {self.rule}
Error message
optional field cannot be sequence or slice: {self.rule} What it means
Optionality and sequences are mutually exclusive in the generated AST. FieldType.__init__ rejects rules that combine a trailing '?' with any collection form (T*, Vec<T>, ThinVec<T>, Box<[T]>), because the generated accessors, visitors and constructors have no representation for an optional sequence.
Source
Thrown at crates/ruff_python_ast/generate.py:280
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
def write_preamble(out: list[str]) -> None:
out.append("""
// This is a generated file. Don't modify it by hand!
// Run `crates/ruff_python_ast/generate.py` to re-generate the file.
use crate::name::Name;View on GitHub (pinned to d1087a4b9e)
Solutions
- Drop the '?': sequences are inherently empty-able, so 'Box<[T]>' or 'Vec<T>' (defaulting to empty) is the intended modeling.
- If absence vs. emptiness genuinely differs, restructure the node (e.g. a wrapper node or an enum) instead of an optional sequence.
- Re-run the generator after fixing the rule.
Example fix
# before docstring: Box<[str]>? # after (sequences cannot be optional; empty means absent) docstring: Box<[str]>
Defensive patterns
Strategy: validation
Validate before calling
SEQUENCE_PREFIXES = ('Vec<', 'ThinVec<', 'Box<[')
def rule_ok(rule: str) -> bool:
base = rule.rstrip('?')
return not (rule.endswith('?') and base.startswith(SEQUENCE_PREFIXES)) Prevention
- Model 'maybe empty' with an empty sequence, not Option<Vec<...>>.
- If absence must differ from emptiness, restructure the node instead of fighting the DSL.
When it happens
Trigger: Writing a rule such as 'Stmt*?' or 'Vec<Expr>?' — a trailing '?' on a rule that split_sequence_type() classifies as a sequence.
Common situations: Trying to model 'maybe-empty-or-absent' lists; porting hand-written structs that used Option<Vec<...>>; making a formerly-optional field a list during a grammar change.
Related errors
- `?` must be at the end: {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/6cbbf36796e1065c.
Report an issue: GitHub.