astral-sh/ruff · warning
Expected Expression::Tuple | Expression::List
Error message
Expected Expression::Tuple | Expression::List
What it means
This error is raised internally by ruff's flake8_comprehensions rule C408 (unnecessary dict literal) when generating an automatic fix. The fixer expects the single argument to `dict()` to be a tuple or list literal whose elements become dict entries; if the argument is any other expression kind, the fix cannot be safely constructed and the fix aborts with this message.
Source
Thrown at crates/ruff_linter/src/rules/flake8_comprehensions/fixes.rs:170
))
}
/// (C406) Convert `dict([(1, 2)])` to `{1: 2}`.
pub(crate) fn fix_unnecessary_literal_dict(expr: &Expr, checker: &Checker) -> Result<Edit> {
let locator = checker.locator();
let stylist = checker.stylist();
// Expr(Call(List|Tuple)))) -> Expr(Dict)))
let module_text = locator.slice(expr);
let mut tree = match_expression(module_text)?;
let call = match_call_mut(&mut tree)?;
let arg = match_arg(call)?;
let elements = match &arg.value {
Expression::Tuple(inner) => &inner.elements,
Expression::List(inner) => &inner.elements,
_ => {
bail!("Expected Expression::Tuple | Expression::List");
}
};
let elements: Vec<DictElement> = elements
.iter()
.map(|element| {
if let Element::Simple {
value: Expression::Tuple(tuple),
comma,
} = element
{
if let Some(Element::Simple { value: key, .. }) = tuple.elements.first() {
if let Some(Element::Simple { value, .. }) = tuple.elements.get(1) {
return Ok(DictElement::Simple {
key: key.clone(),
value: value.clone(),
comma: comma.clone(),
whitespace_before_colon: ParenthesizableWhitespace::default(),View on GitHub (pinned to 26f38c119c)
Solutions
- Verify the argument inside `dict(...)` is a literal tuple or list of key/value pairs; rewrite it as a dict literal `{'a': 1}` manually if the fixer declines.
- Skip autofix for this diagnostic (`ruff check --fix --extend-ignore C408` or per-line noqa) and refactor by hand.
- Upgrade ruff; the matcher was tightened over time so unsupported shapes bail earlier and more predictably.
Example fix
// before (fixer input)
dict([("a", 1), ("b", 2)])
// after (what the fixer produces)
{"a": 1, "b": 2} Defensive patterns
Strategy: fallback
Validate before calling
import ast
node = ast.parse('dict([("a", 1)])').body[0].value
ok = isinstance(node.args[0], (ast.Tuple, ast.List)) if node.args else False Type guard
def is_seq_literal(expr):
return isinstance(expr, (ast.Tuple, ast.List)) Try / catch
try:
apply_autofix(diagnostic)
except Exception:
manual_rewrite() # keep the diagnostic, fix by hand Prevention
- Pass only literal tuples/lists of pairs to dict() when targeting C408 autofix
- Re-run ruff after edits so fixes see fresh ASTs
- Keep C408 autofix on simple literal forms
When it happens
Trigger: Running `ruff check --fix` on code like `dict((('a', 1),))` or `dict(x)` where the argument to `dict()` is not an AST `Expression::Tuple` or `Expression::List` (e.g. a name, call, or generator expression).
Common situations: Fixing code where `dict()` receives a variable or a function call rather than a literal sequence; running autofix on dynamically generated code; rare because the diagnostic C408 normally only fires on tuple/list literals.
Related errors
- Expected each argument to be a tuple of length two
- Expected one argument in outer function call
- Expected at least one argument in outer function call
- Expected Expression::ListComp | Expression:SetComp | Express
- Expected two arguments
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/5ddb2269f598d8d9.
Report an issue: GitHub.