{"record":{"id":"af370258b5116c11","repo":"nvbn/thefuck","slug":"got-an-unexpected-keyword-argument","errorCode":null,"errorMessage":"got an unexpected keyword argument '{}'","messagePattern":"got an unexpected keyword argument '(.+?)'","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"thefuck/utils.py","lineNumber":180,"sourceCode":"        else:\n            if should_yield and line:\n                yield line.strip()\n\n\ndef replace_command(command, broken, matched):\n    \"\"\"Helper for *_no_command rules.\"\"\"\n    new_cmds = get_close_matches(broken, matched, cutoff=0.1)\n    return [replace_argument(command.script, broken, new_cmd.strip())\n            for new_cmd in new_cmds]\n\n\n@memoize\ndef is_app(command, *app_names, **kwargs):\n    \"\"\"Returns `True` if command is call to one of passed app names.\"\"\"\n\n    at_least = kwargs.pop('at_least', 0)\n    if kwargs:\n        raise TypeError(\"got an unexpected keyword argument '{}'\".format(kwargs.keys()))\n\n    if len(command.script_parts) > at_least:\n        return os.path.basename(command.script_parts[0]) in app_names\n\n    return False\n\n\ndef for_app(*app_names, **kwargs):\n    \"\"\"Specifies that matching script is for one of app names.\"\"\"\n    def _for_app(fn, command):\n        if is_app(command, *app_names, **kwargs):\n            return fn(command)\n        else:\n            return False\n\n    return decorator(_for_app)\n\n","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/nvbn/thefuck/blob/c7e7e1d884d3bb241ea6448f72a989434c2a35ec/thefuck/utils.py#L162-L198","documentation":"This TypeError is raised by thefuck's rule-helper is_app() (thefuck/utils.py) whenever it receives a keyword argument other than the single supported one, 'at_least'. is_app pops 'at_least' from kwargs and, if anything remains, raises with the leftover key names. It usually surfaces indirectly: the @for_app decorator forwards its own **kwargs straight into is_app, so any unsupported keyword passed to @for_app in a custom or third-party rule blows up here when the rule is evaluated against a command.","triggerScenarios":"Writing a thefuck rule with an unsupported decorator keyword, e.g. @for_app('apt-get', atleast=2) (misspelled) or @for_app('git', not_a_kwarg=True). The error also fires when calling utils.is_app(command, 'cargo', at_least=1, bogus=0) directly. Note the message interpolates kwargs.keys(), so on Python 3 it renders as \"got an unexpected keyword argument 'dict_keys([...'bogus'])'\" — the real offending key is inside that repr.","commonSituations":"Custom rules in ~/.config/thefuck/rules written against an older/newer thefuck API where decorator keywords changed; copy-pasted rules from blog posts or other thefuck versions that use keywords the installed version does not support; misspelling 'at_least' (e.g. 'atleast' or 'atLeast'); passing positional-arity hints that were removed. The traceback typically points at the rule module during `thefuck` correction matching, since for_app -> is_app runs for every candidate rule.","solutions":["Open the rule file named in the traceback and change the decorator to use only supported keywords: @for_app('prog', at_least=N) with no other kwargs; remove or delete unsupported ones.","Check the spelling: the only accepted keyword is exactly 'at_least' (underscore, lowercase).","If the rule came from a rules pack or blog post, check it against the thefuck version you have installed (git repo rules/ directory or CHANGELOG) and update the rule to that version's API.","Upgrade/downgrade thefuck so its utils match the rule set you are using (pip install -U thefuck), then reload with `thefuck --alias` in a fresh shell."],"exampleFix":"# before (~/.config/thefuck/rules/my_rule.py)\nfrom thefuck.utils import for_app\n\n@for_app('docker', atleast=2)   # misspelled keyword\ndef match(command):\n    return 'is not a docker command' in command.output.lower()\n\n# after\nfrom thefuck.utils import for_app\n\n@for_app('docker', at_least=2)  # only 'at_least' is supported\ndef match(command):\n    return 'is not a docker command' in command.output.lower()","handlingStrategy":"validation","validationCode":"# Validate rule decorators before thefuck loads them (e.g. in a rules smoke test):\nimport inspect\nfrom thefuck.utils import for_app\n\nSUPPORTED = {'at_least'}\n\ndef check_rule_kwargs(fn):\n    # Re-create the kwargs your rule passes to for_app and verify them\n    passed = getattr(fn, '_for_app_kwargs', {})  # set by your own wrapper if needed\n    bad = set(passed) - SUPPORTED\n    assert not bad, f\"Unsupported @for_app kwargs: {bad}; only 'at_least' is valid\"\n\n# Simpler: grep your rules dir for misspellings before starting thefuck\nimport re, pathlib\nfor p in pathlib.Path('~/.config/thefuck/rules').expanduser().glob('*.py'):\n    src = p.read_text()\n    for m in re.finditer(r'@for_app\\(([^)]*)\\)', src):\n        for kw in re.findall(r'(\\w+)\\s*=', m.group(1)):\n            assert kw == 'at_least', f'{p.name}: bad kwarg {kw!r} in @for_app'","typeGuard":"# Python narrowing helper for direct is_app/for_app callers\n\ndef valid_for_app_kwargs(kwargs: dict) -> bool:\n    \"\"\"True iff kwargs are accepted by thefuck's is_app (only 'at_least').\"\"\"\n    return set(kwargs) <= {'at_least'} and isinstance(kwargs.get('at_least', 0), int)\n\n# usage before calling:\nif not valid_for_app_kwargs({'atleast': 2}):\n    raise TypeError('only at_least=<int> may be passed to @for_app')","tryCatchPattern":"# Wrap custom rule loading / correction so a bad rule reports itself instead of crashing:\ndef safe_match(rule_module, command):\n    try:\n        return rule_module.match(command)\n    except TypeError as e:\n        if 'unexpected keyword argument' in str(e):\n            print(f\"rule {rule_module.__name__} uses unsupported @for_app kwargs: {e}\")\n            return False  # let other rules still run\n        raise","preventionTips":["Use only the documented keyword: @for_app('prog', at_least=N) — nothing else.","Smoke-test your rules after every thefuck upgrade: run `thefuck <broken-cmd>` once in CI or manually to catch API drift.","Pin the thefuck version in your dotfiles so rule syntax and utils API stay in sync; read that version's rules/ for reference.","When copying rules from the internet, diff their decorator keywords against the bundled rules in the installed thefuck package before dropping them into ~/.config/thefuck/rules."],"tags":["thefuck","python","kwargs","typeerror","rule-config","argument-validation"],"backgroundTag":null,"analyzedSha":"c7e7e1d884d3bb241ea6448f72a989434c2a35ec","analyzedAt":"2026-08-14T19:43:00.961Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}