astral-sh/ruff · info

Unknown keyword argument

Error message

Unknown keyword argument

What it means

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.

Source

Thrown at crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs:248

    fn args_map<'a>(
        &'a self,
        args: &'a [Expr],
        keywords: &'a [Keyword],
    ) -> Result<FxHashMap<&'a str, &'a Expr>> {
        // If we have variable-length arguments, abort.
        if args.iter().any(Expr::is_starred_expr) || keywords.iter().any(|kw| kw.arg.is_none()) {
            bail!("Variable-length arguments are not supported");
        }

        let arg_spec = self.arg_spec();

        // If any of the keyword arguments are not in the argument spec, abort.
        if keywords.iter().any(|kw| {
            kw.arg
                .as_ref()
                .is_some_and(|kwarg_name| !arg_spec.contains(&kwarg_name.as_str()))
        }) {
            bail!("Unknown keyword argument");
        }

        // Generate a map from argument name to value.
        let mut args_map: FxHashMap<&str, &Expr> =
            FxHashMap::with_capacity_and_hasher(args.len() + keywords.len(), FxBuildHasher);

        // Process positional arguments.
        for (arg_name, value) in arg_spec.iter().zip(args) {
            args_map.insert(arg_name, value);
        }

        // Process keyword arguments.
        for arg_name in arg_spec.iter().skip(args.len()) {
            if let Some(value) = keywords.iter().find_map(|keyword| {
                if keyword
                    .arg
                    .as_ref()
                    .is_some_and(|kwarg_name| &kwarg_name == arg_name)

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. Correct or remove the unknown keyword so the call matches the assertion method's real signature
  2. Add `# noqa: PT009` if the extra keyword is intentional and the call should stay untouched
  3. Exclude the rule/fix for that path in `pyproject.toml` (`lint.per-file-ignores` or `lint.ignore = ["PT009"]`)

Example fix

// before
self.assertRaises(ValueError, msgg='bad input')
// after
self.assertRaises(ValueError, msg='bad input')
Defensive patterns

Strategy: validation

Validate before calling

import inspect, ast

def keywords_in_signature(call: ast.Call) -> bool:
    names = {kw.arg for kw in call.keywords if kw.arg is not None}
    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)

Type guard

def has_known_kwargs(node: ast.Call) -> bool:
    return all(kw.arg is not None for kw in node.keywords) and keywords_in_signature(node)

Prevention

When it happens

Trigger: `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')`.

Common situations: 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.

Related errors


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-09-05). Data as JSON: /api/errors/a53c3758f05f3ed6. Report an issue: GitHub.