nvbn/thefuck · error · TypeError
got an unexpected keyword argument '{}'
Error message
got an unexpected keyword argument '{}' What it means
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.
Source
Thrown at thefuck/utils.py:180
else:
if should_yield and line:
yield line.strip()
def replace_command(command, broken, matched):
"""Helper for *_no_command rules."""
new_cmds = get_close_matches(broken, matched, cutoff=0.1)
return [replace_argument(command.script, broken, new_cmd.strip())
for new_cmd in new_cmds]
@memoize
def is_app(command, *app_names, **kwargs):
"""Returns `True` if command is call to one of passed app names."""
at_least = kwargs.pop('at_least', 0)
if kwargs:
raise TypeError("got an unexpected keyword argument '{}'".format(kwargs.keys()))
if len(command.script_parts) > at_least:
return os.path.basename(command.script_parts[0]) in app_names
return False
def for_app(*app_names, **kwargs):
"""Specifies that matching script is for one of app names."""
def _for_app(fn, command):
if is_app(command, *app_names, **kwargs):
return fn(command)
else:
return False
return decorator(_for_app)
View on GitHub (pinned to c7e7e1d884)
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.
Example fix
# before (~/.config/thefuck/rules/my_rule.py)
from thefuck.utils import for_app
@for_app('docker', atleast=2) # misspelled keyword
def match(command):
return 'is not a docker command' in command.output.lower()
# after
from thefuck.utils import for_app
@for_app('docker', at_least=2) # only 'at_least' is supported
def match(command):
return 'is not a docker command' in command.output.lower() Defensive patterns
Strategy: validation
Validate before calling
# Validate rule decorators before thefuck loads them (e.g. in a rules smoke test):
import inspect
from thefuck.utils import for_app
SUPPORTED = {'at_least'}
def check_rule_kwargs(fn):
# Re-create the kwargs your rule passes to for_app and verify them
passed = getattr(fn, '_for_app_kwargs', {}) # set by your own wrapper if needed
bad = set(passed) - SUPPORTED
assert not bad, f"Unsupported @for_app kwargs: {bad}; only 'at_least' is valid"
# Simpler: grep your rules dir for misspellings before starting thefuck
import re, pathlib
for p in pathlib.Path('~/.config/thefuck/rules').expanduser().glob('*.py'):
src = p.read_text()
for m in re.finditer(r'@for_app\(([^)]*)\)', src):
for kw in re.findall(r'(\w+)\s*=', m.group(1)):
assert kw == 'at_least', f'{p.name}: bad kwarg {kw!r} in @for_app' Type guard
# Python narrowing helper for direct is_app/for_app callers
def valid_for_app_kwargs(kwargs: dict) -> bool:
"""True iff kwargs are accepted by thefuck's is_app (only 'at_least')."""
return set(kwargs) <= {'at_least'} and isinstance(kwargs.get('at_least', 0), int)
# usage before calling:
if not valid_for_app_kwargs({'atleast': 2}):
raise TypeError('only at_least=<int> may be passed to @for_app') Try / catch
# Wrap custom rule loading / correction so a bad rule reports itself instead of crashing:
def safe_match(rule_module, command):
try:
return rule_module.match(command)
except TypeError as e:
if 'unexpected keyword argument' in str(e):
print(f"rule {rule_module.__name__} uses unsupported @for_app kwargs: {e}")
return False # let other rules still run
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
AI-assisted analysis of nvbn/thefuck@c7e7e1d884 (2026-08-14).
Data as JSON: /api/errors/af370258b5116c11.
Report an issue: GitHub.