astral-sh/ruff · critical

Arguments should be non-empty

Error message

Arguments should be non-empty

What it means

When building the replacement dict literal for C408, the fix copies the whitespace after the last call argument into the closing brace. The diagnostic only fires for non-empty calls, so `call.args.last()` is assumed to exist; if a fix is applied to an empty `dict()`, this expect panics.

Source

Thrown at crates/ruff_linter/src/rules/flake8_comprehensions/fixes.rs:291

            value: arg.value.clone(),
            comma: arg.comma.clone(),
            whitespace_before_colon: ParenthesizableWhitespace::default(),
            whitespace_after_colon: ParenthesizableWhitespace::SimpleWhitespace(SimpleWhitespace(
                " ",
            )),
        })
        .collect();

    tree = Expression::Dict(Box::new(Dict {
        elements,
        lbrace: LeftCurlyBrace {
            whitespace_after: call.whitespace_before_args.clone(),
        },
        rbrace: RightCurlyBrace {
            whitespace_before: call
                .args
                .last()
                .expect("Arguments should be non-empty")
                .whitespace_after_arg
                .clone(),
        },
        lpar: vec![],
        rpar: vec![],
    }));

    Ok(Edit::range_replacement(
        pad_expression(
            tree.codegen_stylist(stylist),
            expr.range(),
            checker.locator(),
            checker.semantic(),
        ),
        expr.range(),
    ))
}

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Report the crash to Ruff with the minimal input
  2. Upgrade/downgrade to a Ruff version where the empty-call fix is guarded
  3. Suppress C408 for the offending line (`# noqa: C408`) until fixed
  4. Manually replace `dict()` with `{}`

Example fix

// before
let last = call.args.last().expect("Arguments should be non-empty");
// after
let Some(last) = call.args.last() else { return Err(anyhow!("C408 fix requires at least one argument")) };
Defensive patterns

Strategy: try-catch

Validate before calling

# don't emit/apply C408 fixes for empty calls
if src_contains_call('dict', ''):
    print('empty dict() — replace with {} manually instead of --fix')

Type guard

def has_args(call_src: str) -> bool:
    inner = call_src[call_src.index('(')+1:call_src.rindex(')')].strip()
    return len(inner) > 0

Prevention

When it happens

Trigger: Applying the C408 fix to a `dict()` call with zero arguments — a mismatch between the diagnostic condition (which should require arguments) and the fix path.

Common situations: Running `ruff --fix` over code containing `dict()` where an empty-args case leaked through detection; version regressions in the flake8-comprehensions rules.

Related errors


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