{"record":{"id":"7049c5baaeeffab4","repo":"astral-sh/ruff","slug":"expected-dictionary-argument-to-be-kwarg","errorCode":null,"errorMessage":"Expected dictionary argument to be kwarg","messagePattern":"Expected dictionary argument to be kwarg","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/ruff_linter/src/rules/flake8_comprehensions/fixes.rs","lineNumber":255,"sourceCode":"    // below.\n    let mut arena: Vec<String> = vec![];\n\n    let quote = checker\n        .interpolated_string_quote_style()\n        .unwrap_or(stylist.quote());\n\n    // Quote each argument.\n    //\n    // Python normalizes identifiers to NFKC, but string literals are not normalized. Emitting the\n    // raw source text of a keyword argument would change the dictionary key at runtime, so the\n    // name has to be normalized. See https://github.com/astral-sh/ruff/issues/16234.\n    for arg in &call.args {\n        let quoted = format!(\n            \"{}{}{}\",\n            quote,\n            arg.keyword\n                .as_ref()\n                .expect(\"Expected dictionary argument to be kwarg\")\n                .value\n                .nfkc(),\n            quote,\n        );\n        arena.push(quoted);\n    }\n\n    let elements = call\n        .args\n        .iter()\n        .enumerate()\n        .map(|(i, arg)| DictElement::Simple {\n            key: Expression::SimpleString(Box::new(SimpleString {\n                value: &arena[i],\n                lpar: vec![],\n                rpar: vec![],\n            })),\n            value: arg.value.clone(),","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/astral-sh/ruff/blob/26f38c119cac42e4d320ba08f09224fdec74af2c/crates/ruff_linter/src/rules/flake8_comprehensions/fixes.rs#L237-L273","documentation":"The fix for flake8-comprehensions C408 (unnecessary `dict()`/`tuple()` call) rewrites `dict(a=1)` into `{'a': 1}`. It assumes every argument in the call was already verified to be a keyword argument during diagnostic emission; if a positional or starred argument slips into the fix stage, `arg.keyword` is None and this expect panics.","triggerScenarios":"Applying an unsafe fix for C408 (`ruff check --fix`) on a `dict(...)` call whose args include something without a keyword — e.g. `dict(**kwargs)`-style or a diagnostic emitted without re-checking positional/starred args (an internal fix-diagnostic mismatch).","commonSituations":"Users running `ruff --fix` on code like `dict(x, a=1)` or `dict(*args)` where detection and fix disagree, producing a crash during fixing.","solutions":["Report the file to Ruff — the fix must not be offered for calls with positional/starred args","Upgrade Ruff to a version where the C408 fix/diagnostic mismatch is fixed","Skip the fix for that file (`ruff check --fix --no-cache <file>` avoided; use `# noqa: C408` or exclude the rule)","Manually rewrite `dict(...)` to a dict literal as a workaround"],"exampleFix":"// before\nlet key = arg.keyword.as_ref().expect(\"Expected dictionary argument to be kwarg\");\n// after\nlet Some(key) = arg.keyword.as_ref() else { return Err(anyhow!(\"positional arg in dict() fix\")) };","handlingStrategy":"try-catch","validationCode":"# avoid triggering the C408 fix on calls with positional/starred args\nimport re\nif re.search(r'\\bdict\\((\\*|[^)=,]+\\s*,\\s*)', src):\n    print('dict() call has positional/starred args; fix may crash — rewrite manually')","typeGuard":"def is_safe_c408_target(call_src: str) -> bool:\n    args = call_src[call_src.index('(')+1:call_src.rindex(')')].strip()\n    return bool(args) and '*' not in args and all('=' in a for a in args.split(','))","tryCatchPattern":null,"preventionTips":["Write dict literals directly instead of dict(k=v)","Run `ruff check --fix` with --diff first to preview unsafe fixes","Keep Ruff updated so fix/diagnostic mismatches are patched"],"tags":["rust","panic","flake8-comprehensions","autofix","c408"],"backgroundTag":"autofix-invariant-violation","analyzedSha":"26f38c119cac42e4d320ba08f09224fdec74af2c","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}