{"record":{"id":"a53c3758f05f3ed6","repo":"astral-sh/ruff","slug":"unknown-keyword-argument","errorCode":null,"errorMessage":"Unknown keyword argument","messagePattern":"Unknown keyword argument","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs","lineNumber":248,"sourceCode":"    fn args_map<'a>(\n        &'a self,\n        args: &'a [Expr],\n        keywords: &'a [Keyword],\n    ) -> Result<FxHashMap<&'a str, &'a Expr>> {\n        // If we have variable-length arguments, abort.\n        if args.iter().any(Expr::is_starred_expr) || keywords.iter().any(|kw| kw.arg.is_none()) {\n            bail!(\"Variable-length arguments are not supported\");\n        }\n\n        let arg_spec = self.arg_spec();\n\n        // If any of the keyword arguments are not in the argument spec, abort.\n        if keywords.iter().any(|kw| {\n            kw.arg\n                .as_ref()\n                .is_some_and(|kwarg_name| !arg_spec.contains(&kwarg_name.as_str()))\n        }) {\n            bail!(\"Unknown keyword argument\");\n        }\n\n        // Generate a map from argument name to value.\n        let mut args_map: FxHashMap<&str, &Expr> =\n            FxHashMap::with_capacity_and_hasher(args.len() + keywords.len(), FxBuildHasher);\n\n        // Process positional arguments.\n        for (arg_name, value) in arg_spec.iter().zip(args) {\n            args_map.insert(arg_name, value);\n        }\n\n        // Process keyword arguments.\n        for arg_name in arg_spec.iter().skip(args.len()) {\n            if let Some(value) = keywords.iter().find_map(|keyword| {\n                if keyword\n                    .arg\n                    .as_ref()\n                    .is_some_and(|kwarg_name| &kwarg_name == arg_name)","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs#L230-L266","documentation":"Raised by `args_map` in the flake8-pytest-style unittest-assert rewriter when a keyword argument of an `assertX(...)` call is not present in the method's argument spec (`arg_spec()`). The rewriter can only rewrite calls whose keywords it recognizes as real parameters of the assertion method, so an unrecognized keyword aborts the fix generation while the PT009 diagnostic itself is still emitted.","triggerScenarios":"`ruff check --fix` on a call like `self.assertEqual(a, b, custom_flag=True)` (a keyword that is not part of `assertEqual`'s known spec) or a misspelled keyword such as `self.assertRaises(Exception, msgg='x')`.","commonSituations":"Typos in keyword names, keywords from subclasses or stubs the spec doesn't know, and code written against third-party test frameworks that mirror unittest APIs.","solutions":["Correct or remove the unknown keyword so the call matches the assertion method's real signature","Add `# noqa: PT009` if the extra keyword is intentional and the call should stay untouched","Exclude the rule/fix for that path in `pyproject.toml` (`lint.per-file-ignores` or `lint.ignore = [\"PT009\"]`)"],"exampleFix":"// before\nself.assertRaises(ValueError, msgg='bad input')\n// after\nself.assertRaises(ValueError, msg='bad input')","handlingStrategy":"validation","validationCode":"import inspect, ast\n\ndef keywords_in_signature(call: ast.Call) -> bool:\n    names = {kw.arg for kw in call.keywords if kw.arg is not None}\n    return bool(names) and all(n in {'msg', 'msg_pattern', 'expected_regex', 'expected_exception', 'places', 'delta', 'seq', 'members', 'container', 'subset', 'superset'} for n in names)","typeGuard":"def has_known_kwargs(node: ast.Call) -> bool:\n    return all(kw.arg is not None for kw in node.keywords) and keywords_in_signature(node)","tryCatchPattern":null,"preventionTips":["Only pass keywords documented for the assertion method; verify spelling (e.g. `msg`, not `msgg`)","Run ruff with `--diff` to see which findings get fixes before applying them","Suppress PT009 deliberately with noqa when extending assertion APIs in test base classes"],"tags":["ruff","flake8-pytest-style","autofix","keyword-arguments"],"backgroundTag":"linter-autofix-skipped","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}