Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

The Fuck — User Guide

Generated April 2026 from nvbn/thefuck at c7e7e1d

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

The Fuck is a magnificent terminal companion that corrects errors in your previous console command. Type the wrong thing? Just type fuck and let it suggest — and run — the right one.

1. Quick Start

The Fuck reads the most recent failed command, runs it through a catalog of more than 160 correction rules, and offers the most likely fix. Press Enter to run it; press up/down to cycle through alternatives; press Ctrl+C to abort.

Key features

  • Shell-agnostic alias. Works in Bash, Zsh, Fish, Tcsh, and PowerShell — one command (thefuck --alias) emits the right wrapper for whichever shell you are in.
  • 160+ built-in correction rules for Git, Docker, npm, pip, Homebrew, apt, dnf, pacman, AWS CLI, Azure CLI, Heroku, Vagrant, Terraform, systemd, Maven, Gradle, Yarn, and many more.
  • Plugin architecture. Drop a Python file into ~/.config/thefuck/rules/ and it joins the rotation. Third-party packages named thefuck_contrib_* are auto-discovered too.
  • Multi-suggestion UI. When several rules match, browse the alternatives without retyping anything.
  • Side-effect aware. Some fixes can do extra cleanup (delete a bad SSH key line, prune a half-extracted tarball) — the prompt marks those with (+side effect).
  • History rewriting. The corrected command is written into your shell history so the up-arrow brings up the fix, not the typo (Bash, Zsh, Fish, Tcsh).
  • Configurable via a Python file at ~/.config/thefuck/settings.py and via THEFUCK_* environment variables.

Prerequisites

Python 3.5 or later (Python 2.7 is also supported but deprecated), a supported shell, and pip 6.0+.

Install in one line

pip install --user thefuck

Then add the alias to your shell config and reload. For example, in ~/.zshrc:

eval $(thefuck --alias)

Tip

You can rename the alias to anything you like — eval $(thefuck --alias FUCK) gives you FUCK in caps; eval $(thefuck --alias please) gives a polite alternative. The Fuck respects the TF_ALIAS environment variable internally so all rules keep working.

A first taste

$ git brnch
git: 'brnch' is not a git command. See 'git --help'.

The most similar command is
        branch
$ fuck
git branch [enter/↑/↓/ctrl+c]
* main
  feature/login

You typed git brnch, Git complained, you typed fuck, and the prompt now offers git branch. Hit Enter and it runs.

What to try next

  • Read 2. Installation for package-manager-specific recipes (Homebrew, apt, pkg, scoop, AUR).
  • See 4. Core Usage for the full CLI flag reference and the keyboard controls in the suggestion prompt.
  • Browse 5. Rule Catalog to learn which mistakes The Fuck already knows how to fix.
  • Skim 6. Common Tasks for recipes — auto-confirm, retry on failure, instant-mode, custom rules.

2. Installation

System requirements

Requirement Detail
Python 2.7 or 3.5+ (Python 3.5+ recommended; 2.7 is deprecated)
pip 6.0 or newer (older versions are explicitly rejected by setup.py)
Shell Bash, Zsh, Fish, Tcsh/Csh, or PowerShell/PWSH
OS Linux, macOS, Windows, BSD; FreeBSD/OpenBSD work via pkg/ports
Runtime deps psutil, colorama, six; on Windows also win_unicode_console
Optional pyte and a working script(1) binary for experimental instant mode

Install methods

pip (any platform)

pip install --user thefuck

Homebrew (macOS / Linux)

brew install thefuck

Debian / Ubuntu

sudo apt update
sudo apt install python3-dev python3-pip python3-setuptools
pip3 install --user thefuck

Fedora / RHEL

sudo dnf install python3-devel python3-pip
pip3 install --user thefuck

Arch Linux

sudo pacman -S thefuck

FreeBSD

pkg install thefuck

Source

git clone https://github.com/nvbn/thefuck.git
cd thefuck
pip install --user .

Important

pip install --user puts thefuck and fuck under ~/.local/bin (Linux/macOS) or %APPDATA%\Python\PythonX\Scripts (Windows). If fuck is "not found" after install, that directory is not on your $PATH — see the troubleshooting entry below.

Wire up the shell alias

The Fuck does not modify your shell config automatically on pip install. You add a single line, then reload.

Bash

Append to ~/.bashrc (or ~/.bash_profile on macOS):

eval "$(thefuck --alias)"

Reload:

source ~/.bashrc

Zsh

Append to ~/.zshrc:

eval $(thefuck --alias)

Reload:

source ~/.zshrc

Fish

Append to ~/.config/fish/config.fish:

thefuck --alias | source

Reload:

fish

Tcsh / Csh

Append to ~/.tcshrc:

eval `thefuck --alias`

Reload by exec'ing tcsh.

PowerShell

Append to $profile:

iex "$(thefuck --alias)"

Reload:

. $profile

Tip

You can also let The Fuck configure itself: type fuck once with no alias set and it prints how-to-configure instructions. Type fuck a second time within 60 seconds (controlled by the internal CONFIGURATION_TIMEOUT constant) and it appends the alias line for you in supported shells. Then source the file or restart the shell.

Verify the install

fuck --version
The Fuck 3.32 using Python 3.11.4 and Bash 5.2.15

If you see the version banner — alias resolved, Python ABI is happy, and the shell detection landed on a known shell — installation is complete.

Install troubleshooting

Error: fuck: command not found after install

Cause: pip install --user puts the script in a per-user directory that is not on PATH by default.

Solution:

  1. Find the directory: python3 -m site --user-base then append /bin (Linux/macOS) or \Scripts (Windows).
  2. Add it to PATH. On Bash/Zsh, append to your rc file:
    export PATH="$HOME/.local/bin:$PATH"
    
  3. Restart the shell.

Prevention: Use pipx install thefuck if you prefer isolated CLIs, or install via Homebrew/apt/dnf where the package places binaries on PATH automatically.

Error: ModuleNotFoundError: No module named 'imp' (Python 3.12+)

Cause: Python 3.12 removed the legacy imp module that older releases of The Fuck imported. (Tracked in upstream issues #1434 and #1444.)

Solution:

  1. Upgrade to the latest release: pip install --user --upgrade thefuck.
  2. If still failing, downgrade your Python or use pyenv install 3.11.x and point pip at it.

Prevention: Pin a Python version known to work in your CI (3.10 or 3.11 are safest as of this writing).

Error: ModuleNotFoundError: No module named 'distutils' (Python 3.12+)

Cause: distutils was removed in Python 3.12; the setup.py install path uses pkg_resources which transitively pulls it in. (See issue #1444.)

Solution:

  1. pip install --user setuptools to bring back the shim.
  2. Reinstall: pip install --user --upgrade thefuck.

Error: pip older than 6.0 not supported

Cause: setup.py refuses pip versions below 6.0.

Solution:

pip install -U pip
pip install --user thefuck

Error: zsh: command not found: thefuck (Termux / minimal Linux)

Cause: Termux installs Python scripts under ~/.local/bin but the shell's PATH may not include it. Reported in issue #650 and #1369.

Solution: Add export PATH="$PREFIX/bin:$HOME/.local/bin:$PATH" to ~/.zshrc (Termux uses $PREFIX for the package root) and source it.

Error: PowerShell profile errors after adding the alias

Cause: Some PowerShell profiles run thefuck --alias synchronously on every shell start, which slows startup. Issue #1380.

Solution: Wrap the alias init in a try/catch and consider caching the alias output:

try {
    iex "$(thefuck --alias)"
} catch {
    Write-Warning "thefuck alias could not be loaded: $_"
}

Warning: Config path ~/.thefuck is deprecated. Please move to ~/.config/thefuck

Cause: Older releases used ~/.thefuck/. The current release follows the XDG spec.

Solution:

mkdir -p ~/.config
mv ~/.thefuck ~/.config/thefuck

Error: WSL / Cygwin shell detection lands on Generic instead of Bash

Cause: The Fuck walks the parent process tree to detect the shell. In nested terminals (e.g., tmux inside WSL inside Windows Terminal) the parent name may be unrecognised.

Solution: Force it via env var: export TF_SHELL=bash (or zsh/fish/tcsh/powershell).

Error: Shell logger supports only Linux and macOS

Cause: --shell-logger uses pty.fork() and termios; that path is Linux/macOS only.

Solution: Drop the flag on Windows; instant mode is also Linux/macOS only.

Error: terminal misbehaves after eval $(thefuck --alias) on Zsh

Cause: The alias function exports PYTHONIOENCODING, manipulates fc -ln, and inserts a print -s for history rewriting. If a parent script captures stderr, the colored confirm prompt can leak. Reported in issue #1476.

Solution: Make sure you eval the alias output from an interactive shell only — wrap the line:

[[ $- == *i* ]] && eval $(thefuck --alias)

3. Configuration

The Fuck reads settings from three places, applied in this order (later sources win):

  1. ~/.config/thefuck/settings.py — a Python file auto-created on first run.
  2. THEFUCK_* environment variables.
  3. CLI flags (-y, -d, -r).

The file is plain Python — assign module-level names. The Fuck imports the file via importlib.util and reads only the keys it knows about.

A minimal settings.py

# ~/.config/thefuck/settings.py

# Only run these rules (default: all)
rules = ['git_push', 'git_pull', 'cd_correction', 'sudo']

# Or: keep defaults but disable specific noisy rules
exclude_rules = ['rm_root', 'sudo']

# Skip the y/n prompt — execute the top suggestion immediately
require_confirmation = False

# Push specific rules to higher priority (lower number = earlier)
priority = {'git_push': 100, 'no_command': 9999}

# Accept up to 5 close-match candidates per rule (default 3)
num_close_matches = 5

Common option combinations

Goal Setting
Auto-run the first match without confirming require_confirmation = False
Re-run fuck if the first fix also fails repeat = True
Cap how far back to look in history history_limit = 2000
Disable colors (CI / non-tty) no_colors = True
Disable history rewriting alter_history = False
Whitelist commands that are allowed to take longer slow_commands = ['gradle', 'mvn', './gradlew', 'vagrant']
Stop searching specific PATH dirs (e.g., a slow NFS mount) excluded_search_path_prefixes = ['/mnt/slow']
Override the wait timeout for re-running the failed command wait_command = 5 (seconds)
Override the wait timeout for "slow commands" wait_slow_command = 30
Inject env vars into the rerun (default {LC_ALL=C, LANG=C, GIT_TRACE=1}) env = {'LC_ALL': 'C'}

Note

Most settings are also exposed as THEFUCK_* environment variables so you can tune behavior without editing the file. See 9. Reference → Environment variables for the full list.

Where settings come from at runtime

DEFAULT_SETTINGS  →  settings.py  →  THEFUCK_* env vars  →  CLI flags

If you ever want a fresh settings file, delete it and run fuck again — ~/.config/thefuck/settings.py is regenerated with the default values commented out.

Tip

For deeper architectural detail, the rule plugin protocol, the full settings dictionary, the env-var-to-setting map, and the alias function source code, see the Admin Guide → Configuration Reference and Developer Internals.


4. Core Usage

The fuck alias vs. the thefuck binary

Command What it does
fuck The shell function/alias the install instructions add. It captures $PYTHONIOENCODING, exports the recent shell history, calls thefuck, then evals its stdout. This is what you should type.
thefuck The Python entrypoint the alias wraps. You normally do not run it directly — its argument parsing is designed for the alias environment.
thefuck_firstuse (Windows) / fuck (Linux/macOS, when alias not configured) The "not configured" splash. Prints the how-to-configure-alias message; second run within 60 seconds writes the alias line into your shell config automatically.

CLI flag reference

The Fuck accepts these options (all surface through both the alias and the raw thefuck binary):

Flag Type Purpose
-v, --version switch Print The Fuck X.Y using Python A.B.C and {shell name version} and exit.
-a [NAME], --alias [NAME] optional value Print the shell alias for the current shell. Optional argument lets you rename it (e.g., --alias FUCK).
-l PATH, --shell-logger PATH value Spawn a PTY-wrapped subshell that mirrors all output to PATH. Used internally by experimental instant mode. Linux/macOS only.
--enable-experimental-instant-mode switch Asks the alias to use instant mode (caches command output via shell logger so re-running the failed command is not necessary). Requires the script binary and Python 3.
-h, --help switch Print usage.
-d, --debug switch Verbose tracing — every rule import time, every match attempt, the full settings dump, and timing for Total, Read output, etc.
--force-command CMD value Hidden flag used internally by --repeat to retry a specific command without consulting history.
command ... positional The command to fix. Normally provided automatically by the alias from fc -ln -10 (or PowerShell Get-History -Count 1).
-y, --yes, --yeah, --hard switch Mutually exclusive with -r. Skip the up/down/enter prompt — run the top suggestion immediately.
-r, --repeat switch Mutually exclusive with -y. If the corrected command also fails, run fuck again on its output.

-y and -r cannot be combined — the parser puts them in a mutually_exclusive_group because chaining them is too dangerous (you could ladder destructive corrections without ever seeing a prompt).

The interactive prompt

When more than one rule matches, you see something like:

git push origin master --force-with-lease (+side effect) [enter/↑/↓/ctrl+c]
Key Action
Enter / Return Run the highlighted suggestion.
↑ or k (qwerty) or e (colemak) or Ctrl+N Previous suggestion.
↓ or j (qwerty) or n (colemak) or Ctrl+P Next suggestion.
Ctrl+C or q Abort — print Aborted and do nothing.

Note

Suggestions appear sorted by rule priority (lower number = higher priority — the prompt's first option is the rule with the smallest priority). You can re-rank rules in settings.py via the priority dict.

The (+side effect) suffix marks suggestions whose rule defined a side_effect callable. Side-effect-bearing rules in the catalog include dirty_untar, dirty_unzip, and ssh_known_hosts (which removes the conflicting host-key line from ~/.ssh/known_hosts).

How history rewriting works

When you accept a fix, The Fuck writes the corrected command to your shell history so the up-arrow recall is the right command. The implementation differs per shell:

  • Bash: history -s $TF_CMD
  • Zsh: print -s $TF_CMD
  • Fish: deletes the wrong command (builtin history delete --exact --case-sensitive) and appends the fix to ~/.config/fish/fish_history
  • Tcsh: appends to ~/.history
  • PowerShell / Generic: no history mutation (PowerShell's history API is per-session)

Disable globally with alter_history = False in settings.py or THEFUCK_ALTER_HISTORY=false.

Where The Fuck runs the failing command

By default, when the alias hands the script to thefuck, the engine re-runs the command in a subprocess with LC_ALL=C LANG=C GIT_TRACE=1 to capture deterministic output, then feeds that output into the rule pipeline. There are three modes:

  1. Rerun mode (default). The current command is re-executed. This is why corrections of long commands feel slow — wait_command (default 3 seconds) caps how long it waits, and slow_commands like lein, react-native, gradle, ./gradlew, vagrant get the longer wait_slow_command (default 15 seconds).
  2. Instant mode (THEFUCK_INSTANT_MODE=true or --enable-experimental-instant-mode). The alias replaces itself with a shell logger that records every command's output to a temp file. The Fuck then reads from that log instead of re-running. Only Bash and Zsh on Linux/macOS, and you need the script(1) binary.
  3. Shell logger mode (auto). If a Unix socket is exposed at $SHELL_LOGGER_SOCKET, The Fuck talks to it and asks for the last 5 commands' output. This is for integrations that record their own shell sessions.

Warning

Instant mode is experimental, sets THEFUCK_INSTANT_MODE=True, modifies PS1 to embed an invisible "user command mark", and replaces your shell with a script recording. If PS1 is later overwritten by another tool (e.g., oh-my-zsh theme), instant mode silently falls back to rerun mode and prints a warning to stderr.

Sudo handling

Many rules are wrapped with the @sudo_support decorator. If your script starts with sudo , the wrapper strips it before matching, runs the rule, and re-adds sudo to the suggestion. So sudo apt-get serch foo matches the same apt_get_search rule as apt-get serch foo.

A separate sudo rule fires when the output contains any of ~25 phrases meaning "you need root" — permission denied, must be root, requires superuser, you don't have access to the history db, EACCES, EdsPermissionError, etc. — and prepends sudo (or sudo sh -c "..." if the script contains && or >).

The opposite rule unsudo fires on you cannot perform this operation as root — it strips a leading sudo.


5. Rule Catalog

Rules are Python modules in thefuck/rules/. Each defines match(command) and get_new_command(command). The bundled catalog ships 160+ rules organised below by ecosystem. Rules apply automatically — there is nothing to enable per-rule unless you opted into a restricted rules = [...] list.

Generic / shell built-ins

Rule Fires on Suggests
cd_correction cd typo/path with "no such file or directory" / "can't cd to" Spell-checks each path component against actual sub-directories
cd_mkdir cd missing/dir (any error) mkdir -p missing/dir && cd missing/dir
cd_parent cd.. (typo, no space) cd ..
cd_cs cs path (one-key typo for cd) cd path
cat_dir cat /some/dir ls /some/dir
chmod_x ./script.sh with "permission denied" chmod +x script.sh && ./script.sh
cp_create_destination cp src dest/ when dest/ does not exist mkdir -p dest && cp src dest/
cp_omitting_directory cp src dest complaining "is a directory" cp -a src dest
dry Doubled command (e.g., git git status) Drop the duplicate (priority 900 — fires before cd cd foo)
dirty_untar tar -xf archive.tar.gz extracting into the current dir mkdir -p archive && tar -xf archive.tar.gz -C archive. Side effect: clean up loose files.
dirty_unzip unzip archive.zip with multiple top-level files unzip archive.zip -d archive. Side effect: delete loose files.
fix_alt_space non-breaking space in the command (Alt+Space typo) Replace with regular space
fix_file Last command output names a file:line (Python, bash, JS, Go, Rust, etc.) $EDITOR file +line && original_command (requires $EDITOR set; supports per-language patterns)
has_exists_script script_in_cwd returns "command not found" ./script_in_cwd
history Anything close-matchable to a previous command The closest historic command (priority 9999 — last-resort)
ls_all ls returning empty ls -A
ls_lah Any ls without flags ls -lah
man man 3 foo not found Tries section 2, then foo --help
man_no_space manfoo (missing space) man foo
mkdir_p mkdir a/b/c with "no such file or directory" mkdir -p a/b/c
missing_space_before_subcommand gitstatus (run-on command) git status
no_command First word not on PATH and resembles something on PATH Closest match from history + $PATH
no_such_file mv src dst/missing/path mkdir -p dst/missing && mv src dst/missing/path
open open github.com or "file does not exist" open http://github.com, touch file && open file, or mkdir file && open file
path_from_history "no such file or directory" with a path Replace with a similar path that appears in shell history (priority 800 — fires before generic rules)
quotation_marks Mixed ' and " in script Normalise to "
remove_shell_prompt_literal Script starts with $ (copy-paste from docs) Drop the $ prefix
remove_trailing_cedilla Script ends with ç Drop the trailing character
rm_dir rm dir failing "is a directory" rm -rf dir (or rm -r for hdfs)
rm_root rm / without --no-preserve-root rm / --no-preserve-root (disabled by default — opt-in via rules)
sl_ls Just sl not found ls
sudo Any "permission denied" / "must be root" / "EACCES" message sudo <script> (or sudo sh -c "..." for compound commands)
sudo_command_from_user_path sudo cmd with "command not found" but cmd exists in user PATH sudo env "PATH=$PATH" cmd
unsudo "you cannot perform this operation as root" Strip leading sudo
switch_lang Garbled command typed in wrong keyboard layout (Russian, Ukrainian, Persian, Hebrew, Greek, Korean) Re-encode to a Latin layout
touch touch path/missing/file mkdir -p path/missing && touch path/missing/file
long_form_help Output mentions --help or Run 'foo --help' for details Replace -h with --help or run the suggested command (priority 5000)
wrong_hyphen_before_subcommand tool-sub (hyphen instead of space) tool sub (priority 4500, no output required)

Git

Rule Fires on Suggests
git_add git checkout/diff/etc. file with "did not match any file(s)" — and the file actually exists git add -- file && original
git_add_force git add with "Use -f if you really want to add them" git add --force
git_bisect_usage git bisect typo with usage banner Closest of start, bad, good, skip, next, reset, visualize, replay, log, run, terms
git_branch_0flag git branch 0d typo-branch (zero typed for hyphen) Replace 0 with - (e.g., -d); handles "branch already exists" by adding git branch -D
git_branch_delete git branch -d with "If you are sure you want to delete it" git branch -D
git_branch_delete_checked_out git branch -d/-D feature failing "Cannot delete branch checked out at" git checkout master && git branch -D feature
git_branch_exists git branch newbranch with "already exists" Five alternatives: branch -d && branch, branch -d && checkout -b, branch -D && branch, branch -D && checkout -b, plain git checkout newbranch
git_branch_list git branch list (instead of plain git branch) git branch --delete list && git branch
git_checkout "did not match any file(s)" on checkout (no "git add?" hint) Closest existing branch, or git branch X && git checkout X, or git checkout -b
git_clone_git_clone git clone git clone URL (typo) Drop the duplicate git clone
git_clone_missing A bare git URL pasted without git clone git clone URL
git_commit_add git commit with "no changes added" git commit -a and git commit -p
git_commit_amend Any git commit git commit --amend
git_commit_reset Any git commit git reset HEAD~
git_diff_no_index git diff fileA fileB (two files outside repo) git diff --no-index fileA fileB
git_diff_staged git diff (without --staged) git diff --staged
git_fix_stash git stash typo with usage banner Closest of apply, branch, clear, drop, list, pop, save, show; falls back to git stash save typo
git_flag_after_filename "fatal: bad flag X used after filename" or "option must come before non-option args" Reorder flags before filename
git_help_aliased git help X where X is "aliased to ..." git help <real-command>
git_hook_bypass git am/commit/push ... Add --no-verify (priority 1100; no output required — opt-in by users with strict pre-commit setups)
git_lfs_mistype git lfs typo with "Did you mean this?" Closest LFS subcommand
git_main_master Output mentions 'master' or 'main' Swap one for the other (priority 1200)
git_merge git merge X with "not something we can merge" + "Did you mean this?" The suggested remote branch
git_merge_unrelated git merge X with "refusing to merge unrelated histories" Append --allow-unrelated-histories
git_not_command git typo with "is not a git command. See 'git --help'" + similar list Closest similar git command
git_pull git pull with "set-upstream" hint Run the suggested set-upstream command, then re-pull
git_pull_clone git pull with "Not a git repository" + "Stopping at filesystem boundary" git clone
git_pull_uncommitted_changes git pull with "unstaged changes" / "uncommitted changes" git stash && git pull && git stash pop
git_push git push with "git push --set-upstream" message The exact --set-upstream line, preserving any extra args
git_push_different_branch_names git push with "upstream branch does not match" The recommended git push <remote> <branch>
git_push_force git push rejected because branch is behind git push --force-with-lease (disabled by default — opt-in)
git_push_pull git push rejected because remote is ahead git pull && git push
git_push_without_commits "src refspec X does not match any" git commit -m "Initial commit" && git push
git_rebase_merge_dir "rebase-merge directory" already exists git rebase --continue/--abort/--skip
git_rebase_no_changes git rebase --continue with "No changes" git rebase --skip
git_remote_delete git remote delete name git remote remove name
git_remote_seturl_add git remote set-url name URL with "No such remote" git remote add name URL
git_rm_local_modifications git rm file with "local modifications" git rm --cached file and git rm -f file
git_rm_recursive git rm dir with "without -r" git rm -r dir
git_rm_staged git rm file with "changes staged in the index" git rm --cached file and git rm -f file
git_stash Output contains "or stash them" git stash && original-command
git_stash_pop git stash pop with overwrite warning (priority 900) git add --update && git stash pop && git reset .
git_tag_force git tag X with "already exists" git tag --force X
git_two_dashes "did you mean --option" Replace -option with --option
scm_correction git ... in an hg repo or vice versa Swap to the right SCM

Note

All Git rules use the @git_support decorator. It also handles GitHub's hub wrapper (alias git=hub) and resolves git aliases by parsing trace: alias expansion: ... lines from output (set via the default env = {'GIT_TRACE': '1'}).

Docker, Kubernetes-adjacent, and registries

Rule Fires on Suggests
docker_not_command "is not a docker command" or Usage: docker banner Closest docker (or management) subcommand
docker_image_being_used_by_container docker rmi with "image is being used by running container" docker container rm -f <id> && original
docker_login "access denied" + "may require 'docker login'" docker login && original

Package managers

Rule Fires on Suggests
apt_get cmd: not found / not installed and the system has CommandNotFound sudo apt-get install <pkg> && original
apt_get_search apt-get search foo apt-cache search foo
apt_invalid_operation apt/apt-get/apt-cache with "Invalid operation" Closest valid op (uninstall → remove shortcut)
apt_list_upgradable apt output suggests apt list --upgradable Run apt list --upgradable
apt_upgrade After listing upgradable packages apt upgrade
brew_install brew install foo with "No available formula. Did you mean ..." brew install <suggestion> for each candidate
brew_link brew ln/link with "brew link --overwrite --dry-run" hint Insert --overwrite --dry-run
brew_reinstall brew install foo already installed and up-to-date brew reinstall foo
brew_uninstall brew uninstall/rm/remove with "brew uninstall --force" hint Add --force
brew_unknown_command brew typo with "Unknown command" Closest from Library/Homebrew/cmd and tap-specific commands
brew_update_formula brew update foo (formula vs. brew itself) brew upgrade foo
brew_cask_dependency brew install requiring cask dependencies brew cask install dep1 && brew cask install dep2 && original
cargo Bare cargo (no subcommand) cargo build
cargo_no_command cargo typo with "no such subcommand" + "Did you mean" The suggested cargo subcommand
choco_install choco install foo with "Installing the following packages" choco install foo.install
composer_not_command composer typo with "Did you mean this?" Suggested command, or install → require
conda_mistype conda typo with "Did you mean 'conda ...'" The suggested conda command
dnf_no_such_command dnf typo with "No such command" Closest dnf operation
gem_unknown_command gem typo with "Unknown command" Closest from gem help commands
pip_install pip install foo with "Permission denied" First try --user, then fall back to sudo
pip_unknown_command pip typo with "unknown command, maybe you meant" The suggested pip command
npm_missing_script npm run foo with "missing script: foo" Closest from npm run-script listing
npm_run_script npm foo where foo is in package.json scripts npm run-script foo
npm_wrong_command npm typo with "where <command> is one of" Closest npm command
pacman "not found" with pkgfile available pacman/yay/pikaur/yaourt -S <package> && original
pacman_invalid_option pacman complaining "invalid option '-x'" with a lowercase short flag Uppercase that flag
pacman_not_found pacman/yay/pikaur/yaourt -S with "target not found" Replace package name with pkgfile lookup
yum_invalid_operation yum typo with "No such command" Closest yum op (uninstall → remove)
yarn_alias yarn typo with "Did you mean" The suggested yarn command
yarn_command_not_found yarn typo with "Command not found" Closest from yarn --help (with require→add map)
yarn_command_replaced yarn output with "Run 'X' instead" Run X
yarn_help yarn help cmd with "for documentation about this command" Open the doc URL in default browser
nixos_cmd_not_found "command-not-found" hint with a nix-env -iA pkg line nix-env -iA pkg && original
omnienv_no_such_command pyenv/rbenv/nodenv/goenv with "env: no such command" Common typos (list → versions, remove → uninstall) and closest from <env> commands
python_module_error ModuleNotFoundError: No module named 'X' pip install X && original

Cloud / DevOps CLIs

Rule Fires on Suggests
aws_cli aws ... with "Invalid choice ... maybe you meant" One suggestion per option in the list
az_cli az ... with "is not in the X command group" Each "most similar choice"
heroku_multiple_apps Output mentions multiple-environments doc Re-run with --app <name> for each app
heroku_not_command "Run heroku _ to run" The suggested heroku command
terraform_init "this module is not yet installed" / "initialization required" terraform init && original
terraform_no_command "Terraform has no command named X. Did you mean Y?" Replace X with Y
tsuru_login "not authenticated" + "session has expired" tsuru login && original
tsuru_not_command tsuru typo with "Did you mean?" Suggested tsuru commands
vagrant_up Output suggests "run vagrant up" vagrant up && original (and vagrant up <machine> && original if a machine is implied)
gradle_no_task gradle / gradlew with "Task X is ambiguous/not found" Closest from gradle tasks
gradle_wrapper gradle ... not on PATH but ./gradlew exists ./gradlew args
grunt_task_not_found grunt task with "Warning: Task X not found" Closest from grunt --help
gulp_not_task gulp X with "is not in your gulpfile" Closest from gulp --tasks-simple
lein_not_task lein typo with "is not a task. See 'lein help'" Closest similar Leiningen task
mvn_no_command mvn with "No goals have been specified" mvn clean package and mvn clean install
mvn_unknown_lifecycle_phase mvn typo with "Unknown lifecycle phase" Closest from "Available lifecycle phases"
react_native_command_unrecognized react-native typo with "Unrecognized command" Closest from react-native --help

Languages & build tools

Rule Fires on Suggests
ag_literal ag pattern with "run ag with -Q" ag -Q pattern
cpp11 g++/clang++ with "C++ 2011" / -Wc++11-extensions Append -std=c++11
go_run go run foo (no .go) go run foo.go
go_unknown_command go typo with "unknown command" Closest from go help
grep_arguments_order grep <file> <pattern> failing Move file to the end
grep_recursive grep pattern dir/ failing "is a directory" grep -r pattern dir/
java java foo.java (running compiled code, suffix wrong) java foo (drops .java)
javac javac foo (no .java) javac foo.java
php_s php -s ... (lowercase) php -S ...
python_command ./script.py with "Permission denied" / "command not found" python script.py
python_execute python foo (no .py) python foo.py
test.py (file test.py.py) test.py not found pytest (priority 900 — beats python_command)
prove_recursively prove dir/ with "NOTESTS" prove -r dir/

Networking, SSH, and system utilities

Rule Fires on Suggests
ifconfig_device_not_found ifconfig with "Device not found" Closest from ifconfig -a
ssh_known_hosts ssh/scp with "REMOTE HOST IDENTIFICATION HAS CHANGED!" / DNS-spoofing warnings Re-run the command. Side effect: remove the offending line(s) from the named known_hosts file.
port_already_in_use EADDRINUSE / "Address already in use" patterns kill <pid> && original (uses lsof -i :PORT)
tmux tmux typo with "ambiguous command, could be:" One per suggestion
whois whois URL or whois sub.example.com Strip path / drop subdomain
hostscli hostscli ... with "No such command" / WebsiteImportError Closest of block, unblock, websites, block_all, unblock_all
systemctl systemctl typo with mis-ordered args Swap last two args
unknown_command "X: Unknown command. Did you mean Y?" The suggested command
workon_doesnt_exists workon env for an env that doesn't exist Closest from ~/.virtualenvs/, plus mkvirtualenv env
mercurial hg typo with "did you mean one of" / "is ambiguous" Closest hg command
fab_command_not_found fab task with "Warning: Command(s) not found" Closest from "Available commands:"
sed_unterminated_s sed "s/foo/bar" (missing trailing slash) Add the closing /

Mobile development

Rule Fires on Suggests
adb_unknown_command adb typo with "Android Debug Bridge version" banner Closest of 35+ adb commands

Web / scripting frameworks

Rule Fires on Suggests
django_south_ghost manage.py migrate with "or pass --delete-ghost-migrations" Append --delete-ghost-migrations
django_south_merge manage.py migrate with "--merge: will just attempt the migration" Append --merge
rails_migrations_pending "Migrations are pending. To resolve this issue, run: ..." Run the suggested rake/rails command, then re-run
ln_no_hard_link ln dir target with "hard link not allowed for directory" ln -s dir target
ln_s_order ln -s target source (wrong order, "File exists") Swap positional args

Custom & history-based fallback rules (lowest priority — last-resort)

Rule Priority Notes
path_from_history 800 Replaces a missing path with one that appears in your shell history
cd_cs 900 Single-key cs typo for cd
git_stash_pop 900 Comes before generic stash rules
dry 900 Doubled command (git git status → git status)
test.py 900 test.py not found → pytest
git_hook_bypass 1100 Add --no-verify to am/commit/push
git_main_master 1200 Swap master ↔ main
man_no_space 2000 manfoo → man foo
no_command 3000 Closest executable for typo'd commands
missing_space_before_subcommand 4000 gitstatus → git status
wrong_hyphen_before_subcommand 4500 git-status → git status
long_form_help 5000 cmd -h → cmd --help (or what the help banner suggests)
history 9999 Closest match from valid shell history (very last resort)

Tip

The full list above is the bundled catalog. To see what is actually loaded on your system at runtime, run fuck -d (debug). Each rule's import is logged with timing. Rules that fail to import (missing optional dependency, syntax error in a user rule) are reported as [WARN] Rule X failed to load: followed by the traceback — they are skipped, not fatal.

Authoring your own rule

Drop a ~/.config/thefuck/rules/foo.py with two functions:

# ~/.config/thefuck/rules/uppercase.py
def match(command):
    return command.script.startswith('PYTHON ')

def get_new_command(command):
    return command.script.replace('PYTHON', 'python', 1)

# Optional metadata:
# enabled_by_default = True
# priority = 1000
# requires_output = True   # if False, runs even when there is no output
# def side_effect(old_cmd, new_command_str): ...

The Fuck imports the file with importlib.util.spec_from_file_location, so the only requirement is that match and get_new_command are top-level callables. If get_new_command returns a list instead of a string, the prompt offers each entry as a separate suggestion (priorities are scaled by their order).

For deeper details on the Rule lifecycle, the Command object, the @for_app / @sudo_support / @git_support decorators, side effects, the @cache decorator, and the third-party thefuck_contrib_* discovery mechanism, see the Admin Guide → Developer Internals.


6. Common Tasks

Recipe: Auto-confirm — execute the top suggestion immediately

fuck -y
git push --set-upstream origin master
[main 39e5b1a] ...

Works once. To make it the default everywhere, add require_confirmation = False to ~/.config/thefuck/settings.py or export THEFUCK_REQUIRE_CONFIRMATION=false.

Warning

Auto-confirm is dangerous in scripts that produce ambiguous matches. The Fuck always picks the rule with the smallest priority — if two rules match equally, the alphabetic tiebreaker is the rule filename. For destructive corrections (rm, force-push, ssh known_hosts cleanup), keep the prompt on.

Recipe: Retry until something works

fuck -r

If the corrected command itself fails, The Fuck wraps the suggestion in … || fuck --repeat --force-command "<orig>", so it loops until either a fix succeeds or every rule is exhausted.

You cannot combine -y and -r (the parser refuses).

Recipe: Rename the alias

If fuck is too profane for your workplace:

# In your shell rc:
eval $(thefuck --alias FUCK)
# or:
eval $(thefuck --alias please)
# or even:
eval $(thefuck --alias damnit)

The Fuck's internal TF_ALIAS env var follows the rename, and the "no fucks given" error message becomes "Nothing found" if the alias is anything other than fuck.

Recipe: Disable noisy rules

If sudo keeps suggesting itself for non-sudo problems:

# settings.py
exclude_rules = ['sudo', 'no_command']

Or per-environment:

export THEFUCK_EXCLUDE_RULES="sudo:no_command"

Recipe: Whitelist only a small subset

# settings.py
rules = ['git_push', 'git_pull', 'cd_correction', 'cd_mkdir', 'mkdir_p']

Combine with DEFAULT_RULES to keep all defaults but pin extras:

export THEFUCK_RULES="DEFAULT_RULES:rm_root:git_push_force"

Recipe: Boost a rule's priority so it always wins

# settings.py — smaller numbers fire first
priority = {
    'git_push': 100,        # always before all other git push fixes
    'history':  9999,       # keep history as last-resort
    'no_command': 9000,
}

Or via env:

export THEFUCK_PRIORITY="git_push=100:history=9999"

Recipe: Tame slow commands (avoid the 3-second cutoff)

The default rerun timeout is 3 seconds; any command in slow_commands gets wait_slow_command (15s).

# settings.py
slow_commands = ['gradle', './gradlew', 'mvn', 'lein', 'react-native', 'vagrant', 'docker-compose']
wait_slow_command = 30

Or:

export THEFUCK_SLOW_COMMANDS="gradle:./gradlew:mvn:lein:react-native:vagrant"
export THEFUCK_WAIT_SLOW_COMMAND=30

Recipe: Inject env vars during the rerun

The default is {LC_ALL: C, LANG: C, GIT_TRACE: 1} — LC_ALL=C makes errors deterministic across locales, and GIT_TRACE=1 lets git rules see alias expansions. To add (not replace) entries:

# settings.py
env = {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1', 'NO_COLOR': '1'}

Recipe: Use The Fuck inside Docker / inside a CI pipeline

CI typically lacks a tty, so the prompt would hang. Use:

export THEFUCK_REQUIRE_CONFIRMATION=false
export THEFUCK_NO_COLORS=true
fuck

Or run the binary directly bypassing the alias:

thefuck --yes <last command line here>

Recipe: Try experimental instant mode (Bash/Zsh + Linux/macOS)

Instant mode skips the rerun by recording every command's output to a temp log:

# In your shell rc, replace the eval line with:
eval "$(thefuck --alias --enable-experimental-instant-mode)"

Requirements:

  • Python 3 (instant mode warns and bails on Python 2).
  • The script(1) binary on PATH.
  • A PS1 that does not get rewritten after The Fuck initialised it (oh-my-zsh themes that re-set PS1 on every prompt break instant mode silently).
  • Bash or Zsh — Fish/Tcsh/PowerShell fall back to rerun mode with a warning.

Recipe: Disable history rewriting

Some setups don't want their history mutated:

# settings.py
alter_history = False

The corrected command still runs; it just won't appear via up-arrow.

Recipe: Migrate from ~/.thefuck/ to ~/.config/thefuck/

mkdir -p ~/.config
mv ~/.thefuck ~/.config/thefuck

The Fuck will use the legacy path with a Config path … is deprecated warning if both exist; once migrated, the warning disappears.

Recipe: Add a third-party rule pack

Any pip package whose name starts with thefuck_contrib_ and that exposes a rules/ sub-folder is discovered automatically:

pip install --user thefuck_contrib_my_company

The corrector's get_rules_import_paths() walks every sys.path entry and globs thefuck_contrib_*/rules/.

Recipe: Force a specific shell

If shell auto-detection fails (nested terminals, exotic terminals, embedded PTYs):

export TF_SHELL=zsh    # one of: bash, zsh, fish, csh, tcsh, powershell, pwsh

Recipe: Fix a file at a specific line (the fix_file rule)

$ python my_script.py
  File "my_script.py", line 42
    bad_syntax(
              ^
SyntaxError: unexpected EOF
$ fuck
vim my_script.py +42 && python my_script.py [enter/↑/↓/ctrl+c]

The fix_file rule recognises Python, Bash, JS/Node, Cargo/Rust, Go, GHC/Haskell, Make, Ruby, Zsh, Lua, Awk, Git config, LLC, Perl, and Fish error formats. Requires $EDITOR to be set; respects fixlinecmd and fixcolcmd settings if you want a non-default invocation.


7. Troubleshooting

A diagnostic decision tree:

  1. Does thefuck --version print? → install OK.
  2. Does fuck (with the alias) print "no fucks given"? → alias OK, no rule matched (or every rule that matched produced an empty suggestion).
  3. Does fuck -d show Importing rule X failed? → bad rule file, fix that first.
  4. Is the wrong suggestion winning? → adjust priority in settings.py.
  5. Is the corrected command itself failing? → use fuck -r.

Error: No fucks given

Cause: No rule's match() returned True. Either nothing in the catalog handles your command, or your output cache is empty (instant mode misconfigured).

Solution:

  1. Run fuck -d to see which rules tried and what they saw.
  2. Confirm the alias actually exported TF_HISTORY — the alias should set it to $(fc -ln -10).
  3. If you customised rules = [...], add the rule that should match.

Prevention: Author a custom rule (see Admin Guide → Developer Internals) for command patterns you hit often.

Error: Nothing found

Cause: Same as above, but you renamed the alias — the friendly "no fucks given" only fires when the alias is literally fuck.

Error: Seems like fuck alias isn't configured!

Cause: You ran fuck but the alias is not exported. The "not-configured" entrypoint took over.

Solution:

  1. Run the appropriate eval $(thefuck --alias) for your shell.
  2. Save it to your shell rc and source the file.
  3. Or run fuck a second time within 60 seconds — The Fuck auto-writes the alias if it can detect your shell config.

Error: Shell logger doesn't support your platform.

Cause: --shell-logger (or instant mode) tried to start on Windows, FreeBSD, or a stripped-down Linux without a $SHELL env var.

Solution: Drop --enable-experimental-instant-mode from your alias line; instant mode is Linux/macOS only.

Error: Output log isn't specified (instant mode)

Cause: Instant mode launched but THEFUCK_OUTPUT_LOG was not exported. The shell logger sub-process did not start cleanly.

Solution:

  1. Verify the script(1) binary is on PATH.
  2. Re-source your shell rc.
  3. If the warning persists, drop instant mode — rerun mode is the safer default.

Error: PS1 doesn't contain user command mark

Cause: Instant mode embeds an invisible marker (U+200B × 10) in PS1. Another tool (oh-my-zsh, starship, powerlevel10k) overwrote PS1 after init.

Solution:

  1. Make the eval $(thefuck --alias --enable-experimental-instant-mode) line the last line in your rc.
  2. Or disable instant mode and use the default rerun mode.

Error: Removing possibly out-dated cache

Cause: Switched Python versions; the on-disk shelve cache uses an incompatible dbm format.

Solution: Harmless — The Fuck deletes ~/.cache/thefuck and rebuilds it. Look in XDG_CACHE_HOME (default ~/.cache) if you want to clear it manually.

Error: Can't load settings from file

Cause: ~/.config/thefuck/settings.py raised an exception during import.

Solution:

  1. Run fuck -d and read the printed traceback.
  2. Common culprit: typo'd a setting name (e.g., excluded_rules instead of exclude_rules).
  3. Worst case, delete the file — The Fuck regenerates a defaults-only template on next run.

Error: 'PATH' environment variable not set / no executables discovered

Cause: Some sandboxed runtimes (Docker --env-clean, systemd unit without Environment=PATH) clear $PATH. The no_command rule and the alias both depend on it.

Solution: Set $PATH in the environment before running the alias.

Error: Can't update history (Fish)

Cause: ~/.config/fish/fish_history is read-only or on a read-only FS.

Solution: chmod u+w ~/.config/fish/fish_history. Or set alter_history = False.

Error: Suggestion includes the wrong sudo behaviour

Cause: The sudo rule's pattern list can match unexpected output (e.g., a docs string containing "permission denied").

Solution: Add sudo to exclude_rules, or boost the priority of the rule you want to win:

# settings.py
priority = {'git_push': 100}   # win over sudo

Error: Shell hangs after fuck

Cause: The rerun is waiting on a long-running command (the failed one) to finish. Default timeout is 3 seconds; the slow-command list adds 15.

Solution: Press Ctrl+C — The Fuck kills the process tree. Add the binary to slow_commands so future runs use the longer timeout.

Error: [WARN] Rule X failed to load

Cause: A custom or contrib rule raised an exception at import time.

Solution:

  1. Run python -c "import sys; sys.path.insert(0, '~/.config/thefuck/rules'); import X" to surface the import error.
  2. Either fix the file or move it out of ~/.config/thefuck/rules/.

Error: [WARN] Could not determine shell version

Cause: _get_version() shelled out (e.g., bash -c 'echo $BASH_VERSION') and the subprocess errored.

Solution: Cosmetic — fuck --version will just say "Bash" instead of "Bash 5.2". Investigate by running the underlying command yourself.

Error: Mac Termux "termux errors, but thefuck works"

Cause: Reported as issue #1369. Termux's path conventions throw spurious warnings during alias init.

Solution: Cosmetic; ignore or redirect stderr in your rc:

eval "$(thefuck --alias)" 2>/dev/null

Error: Warning: thefuck-fixed

Cause: Tracked in issue #1570 — a third-party command produces an unparseable output that triggers a misclassified rule.

Solution: Run with -d to see which rule matched; add it to exclude_rules.

Error: Use of deprecated / removed pkg_resources

Cause: setup.py uses pkg_resources.get_distribution which is deprecated in newer setuptools (issue #1552).

Solution: Cosmetic during install. Upgrading setuptools to a recent release silences it. Does not break runtime.

Error: libexpat now an explicit install requirement on macOS

Cause: Issue #1560 — Homebrew Python now requires libexpat to be explicitly linked.

Solution: brew install libexpat then reinstall thefuck.

Error: [self-tests] fail against pytest 9

Cause: Issue #1549 — only relevant if you are running pytest against The Fuck's own test suite. Does not affect end-user behaviour.

Solution: Pin pytest<9 in your dev environment.


8. FAQ

Q: Does The Fuck phone home or upload anything?

No. Everything runs locally. The only network calls come from rules that explicitly need them (e.g., apt_get consults the local CommandNotFound database; pacman_not_found calls local pkgfile). Nothing is sent to a remote service.

Q: What gets cached and where?

The Cache class in thefuck/utils.py writes a shelve database under $XDG_CACHE_HOME/thefuck (default ~/.cache/thefuck). It memoises results of expensive commands like brew --prefix, docker --help, gem help commands, etc., keyed by the modification time of the binary so cache invalidates automatically when you upgrade. Delete the directory to reset.

Q: Can I see what The Fuck would do without running anything?

Run thefuck "<the failing command>" directly. It prints the would-be-evaluated script to stdout but does not eval it. The alias is what actually runs eval.

Q: How do I disable confirmation only for non-destructive rules?

Not directly — require_confirmation is global. The workaround is to keep require_confirmation=True and use -y selectively (fuck -y for one-offs).

Q: Does The Fuck work over SSH?

Yes, as long as you install it on the remote machine and add the alias to that machine's shell rc. There is no client/server protocol.

Q: Does The Fuck work in tmux/screen?

Yes. Shell detection walks the parent process tree, so bash inside tmux resolves correctly. If detection lands on Generic, set TF_SHELL explicitly.

Q: Why does git push sometimes get fixed twice in a row?

Because the first fix added --set-upstream (rule git_push) and the second added --force-with-lease (rule git_push_force, which is disabled by default — but if you opted into it via rules = [...], it can chain). Disable one of them or use priority to choose which fires first.

Q: Why does my shell startup feel slower after adding the alias?

The alias itself is just a function definition — its cost is negligible. The first call to fuck is slower because The Fuck imports every rule file in the catalog. To shave time, set rules = [...] in settings.py to whitelist just the rules you actually use.

Q: Why does The Fuck want script(1) for instant mode?

Instant mode replaces your interactive shell with a script -f recording so it can read command output from the log file instead of re-running. script is part of bsdutils on Debian/Ubuntu, util-linux on Arch, and ships with macOS. If script is missing, instant mode silently falls back to rerun mode.

Q: Can I run The Fuck inside a non-interactive script?

Yes, via the raw thefuck binary plus THEFUCK_REQUIRE_CONFIRMATION=false. But you'll lose the alias's history capture, so pass the failing command as positional args:

THEFUCK_REQUIRE_CONFIRMATION=false thefuck git pus origin main

Q: Does The Fuck support PowerShell on Linux?

Yes — both powershell and pwsh map to the Powershell shell class. The alias uses Get-History -Count 1 for history capture. PowerShell does not support history rewriting (alter_history is a no-op there).

Q: Where is the source for the rule plugin protocol?

thefuck/types.py defines the Rule dataclass and the from_path classmethod that loads each rule file. thefuck/corrector.py is the orchestrator that walks rule paths, filters by is_enabled, and yields suggestions in priority order. See the Admin Guide for a deep dive.

Q: Is there a Windows native build?

pip install thefuck works on Windows. The setup ships scripts/fuck.bat and scripts/fuck.ps1. Bash/Zsh/Fish are not supported on plain Windows; use PowerShell or WSL.

Q: How do I share rules across a team?

Two options:

  1. Publish them as a thefuck_contrib_yourteam pip package — every member's The Fuck auto-discovers it.
  2. Check the rule files into a shared repo and have everyone symlink them into ~/.config/thefuck/rules/.

Q: Can I disable colors permanently?

Yes — no_colors = True in settings.py, or THEFUCK_NO_COLORS=true, or run inside any non-TTY environment (colorama detects this for free).

Q: Why did the alias change my PYTHONIOENCODING?

The alias temporarily exports PYTHONIOENCODING=utf-8 so The Fuck can decode shell output safely on Windows. It saves the original value in TF_PYTHONIOENCODING and restores it after the call. If you see Unicode errors, your shell may be running an outdated alias — re-eval $(thefuck --alias).

Q: How does Git alias expansion work?

Some Git aliases hide the real command (alias.s = status). The Fuck sets GIT_TRACE=1 during the rerun. Git emits trace: alias expansion: s => status to stderr; the @git_support decorator parses that line and rewrites the script before calling the rule's match(). Without GIT_TRACE, Git rules cannot see what the alias resolves to.

Q: Will The Fuck modify my files?

Only via rules that declare a side_effect callable. The bundled side-effect-bearing rules are:

  • dirty_untar — removes loose extracted files in CWD.
  • dirty_unzip — removes loose extracted files in CWD.
  • ssh_known_hosts — deletes the offending line(s) from the named known_hosts file.

These are marked with (+side effect) in the prompt — accept only if you understand what they do.

Q: I heard the project is unmaintained?

Maintenance has slowed (issues #1466 and #1566 discuss this). The codebase is stable and the rule mechanism is extensible enough that you can ship your own rules locally without waiting on upstream.


9. Reference

Environment variables

Variable Maps to setting Type Notes
THEFUCK_RULES rules colon-separated Use DEFAULT_RULES to mean "everything"
THEFUCK_EXCLUDE_RULES exclude_rules colon-separated Disabled rules are skipped at import time
THEFUCK_WAIT_COMMAND wait_command int seconds Default 3
THEFUCK_REQUIRE_CONFIRMATION require_confirmation true/false Default true
THEFUCK_NO_COLORS no_colors true/false Default false
THEFUCK_DEBUG debug true/false Default false
THEFUCK_PRIORITY priority colon-separated key=int pairs e.g., git_push=100:history=9999
THEFUCK_HISTORY_LIMIT history_limit int Default unlimited
THEFUCK_ALTER_HISTORY alter_history true/false Default true
THEFUCK_WAIT_SLOW_COMMAND wait_slow_command int seconds Default 15
THEFUCK_SLOW_COMMANDS slow_commands colon-separated Default lein:react-native:gradle:./gradlew:vagrant
THEFUCK_REPEAT repeat true/false Default false
THEFUCK_INSTANT_MODE instant_mode true/false Default false
THEFUCK_NUM_CLOSE_MATCHES num_close_matches int Default 3
THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES excluded_search_path_prefixes colon-separated Skip these PATH dirs when listing executables

Other env vars used internally

Variable Set by Purpose
TF_ALIAS the shell alias Lets The Fuck recognise its own alias and skip self-corrections
TF_SHELL the shell alias Forces shell detection bypass
TF_HISTORY the shell alias Pre-captured fc -ln -10 output passed in
TF_SHELL_ALIASES the Bash/Zsh alias Pre-captured alias listing for shell-alias resolution
TF_OVERRIDDEN_ALIASES / THEFUCK_OVERRIDDEN_ALIASES user Comma-separated list of Fish aliases that override built-ins (default: cd, grep, ls, man, open)
TF_PYTHONIOENCODING the shell alias Saved $PYTHONIOENCODING; restored after the call
THEFUCK_OUTPUT_LOG instant-mode alias Path to the shell-logger temp file
XDG_CONFIG_HOME OS / user Base for the settings dir (default ~/.config)
XDG_CACHE_HOME OS / user Base for the cache dir (default ~/.cache)
HISTFILE shell Used by Bash/Tcsh/Zsh history reading
EDITOR user Required for the fix_file rule to fire
SHELL OS Used by --shell-logger
PYTHONIOENCODING the shell alias Forced to utf-8 for the duration of the call
SHELL_LOGGER_SOCKET external integration If set, The Fuck reads command output via this Unix socket instead of rerunning

Default settings

DEFAULT_SETTINGS = {
    'rules': [ALL_ENABLED],
    'exclude_rules': [],
    'wait_command': 3,
    'require_confirmation': True,
    'no_colors': False,
    'debug': False,
    'priority': {},
    'history_limit': None,
    'alter_history': True,
    'wait_slow_command': 15,
    'slow_commands': ['lein', 'react-native', 'gradle', './gradlew', 'vagrant'],
    'repeat': False,
    'instant_mode': False,
    'num_close_matches': 3,
    'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'},
    'excluded_search_path_prefixes': [],
}

Files & directories

Path Purpose
~/.config/thefuck/settings.py User Python settings (auto-generated)
~/.config/thefuck/rules/ User custom rules (.py files)
~/.thefuck/ Legacy path (still honoured with a deprecation warning)
$XDG_CACHE_HOME/thefuck shelve cache for memoised commands
/tmp/thefuck-script-log-<uuid> Instant-mode output log
/tmp/thefuck.last_not_configured_run_<user> Tracker for the auto-configure-on-second-run flow

Exit codes

Code Meaning
0 A correction was selected and emitted to stdout (the alias evals it).
1 Nothing matched, or the user pressed Ctrl+C / q.
-1 Python version check failed in setup.py (install-time only).

For architecture, the rule plugin protocol, internals, and contributing, continue to the Admin Guide.