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

  1. 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.
  2. Skip autofix for this diagnostic (`ruff check --fix --extend-ignore C408` or per-line noqa) and refactor by hand.
  3. 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

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


AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05). Data as JSON: /api/errors/5ddb2269f598d8d9. Report an issue: GitHub.