# What Is the `side_effect` Function in a thefuck Rule?

> Learn how the `side_effect` function in thefuck rules cleans up or prepares before executing corrected commands. Understand its essential role in fixing your code.

- Repository: [Vladimir Iakovlev/thefuck](https://github.com/nvbn/thefuck)
- Tags: internals
- Published: 2026-02-27

---

**The `side_effect` function in a thefuck rule performs cleanup or preparatory actions—such as deleting corrupted files or removing invalid SSH host keys—after a user accepts a suggested fix but before the corrected command actually executes.**

The `side_effect` function is an optional component in rules for **nvbn/thefuck**, the popular command-line tool that corrects mistyped console commands. When a rule matches a failed command and generates a fix, this function handles necessary filesystem or environment modifications that fall outside the scope of the command syntax itself, ensuring the retry succeeds in a cleaned environment.

## Purpose of the `side_effect` Function in thefuck Rules

In the thefuck architecture, each rule can define three core functions: `match(command)`, `get_new_command(command)`, and optionally `side_effect(old_cmd, command)`. While `match` determines if the rule applies and `get_new_command` generates the replacement syntax, `side_effect` executes **after** the user confirms the suggestion but **before** the new command runs.

This timing is critical for scenarios where the corrected command would fail if executed immediately against the current system state. The function serves as a bridge between error detection and successful retry execution, performing operations that must happen outside the command itself.

### Common Use Cases for `side_effect`

| Scenario | What `side_effect` Does | Example Rule |
|----------|-------------------------|--------------|
| **SSH host key mismatch** | Parses error output, opens `~/.ssh/known_hosts`, deletes the offending line by line number, writes the file back. | [`thefuck/rules/ssh_known_hosts.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/ssh_known_hosts.py) |
| **Partial unzip cleanup** | Opens the zip archive, identifies extracted files in the current directory, removes them to prevent "file already exists" errors on retry. | [`thefuck/rules/dirty_unzip.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_unzip.py) |
| **Tar extraction residue** | Uses `tarfile.TarFile` to list members and deletes each extracted file before retrying with proper `-C` options. | [`thefuck/rules/dirty_untar.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_untar.py) |

In all three cases, the corrected command (`get_new_command`) would fail again if leftover artifacts remained in place. By cleaning the environment first, the rule guarantees that the user's next attempt succeeds.

## How the thefuck Engine Executes `side_effect`

The execution flow in [`thefuck/rules/__init__.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/__init__.py) and the main engine follows a strict sequence when processing a failed command:

1. **Match Detection**: The engine calls `match(command)` on each rule until one returns `True`.
2. **Fix Generation**: The matching rule's `get_new_command(command)` produces the replacement command string.
3. **User Confirmation**: The engine displays the suggestion and waits for user acceptance.
4. **Side Effect Execution**: If the rule defines `side_effect`, the engine calls `side_effect(old_cmd, command)` with the original `Command` object and the new `Command` object.
5. **Command Execution**: The cleaned-up environment now runs the new command.

The signature `side_effect(old_cmd, command)` gives the function access to:

- `old_cmd.output`, `old_cmd.script`, etc. — the original failing command's output for parsing.
- `command.script` — the corrected command (often not needed, but available).

Because the function can modify files, remove directories, or perform any other OS-side operation, it must be **idempotent** and **safe** (the rules usually check that affected paths stay inside the current working directory).

## Real-World Implementation Examples

### Cleaning SSH Known Hosts in [`ssh_known_hosts.py`](https://github.com/nvbn/thefuck/blob/main/ssh_known_hosts.py)

The `ssh_known_hosts` rule handles host key mismatch errors by parsing the SSH error output to identify the offending line number in `~/.ssh/known_hosts`, then removing that specific line before the retry.

```python
def side_effect(old_cmd, command):
    offending_pattern = re.compile(
        r'(?:Offending (?:key for IP|\S+ key)|Matching host key) in ([^:]+):(\d+)',
        re.MULTILINE)
    offending = offending_pattern.findall(old_cmd.output)
    for filepath, lineno in offending:
        with open(filepath, 'r') as fh:
            lines = fh.readlines()
            del lines[int(lineno) - 1]          # remove the bad key line

        with open(filepath, 'w') as fh:
            fh.writelines(lines)                # write the cleaned file back

```

*Source*: [[`thefuck/rules/ssh_known_hosts.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/ssh_known_hosts.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/rules/ssh_known_hosts.py#L27-L38)

### Removing Partial Unzip Artifacts in [`dirty_unzip.py`](https://github.com/nvbn/thefuck/blob/main/dirty_unzip.py)

When `unzip` fails partway through extraction (leaving incomplete files), the `dirty_unzip` rule cleans up these artifacts before retrying with proper flags.

```python
def side_effect(old_cmd, command):
    # Extracted files are removed, zip file is kept

    with zipfile.ZipFile('sample.zip', 'r') as archive:
        for file in archive.namelist():
            try:
                os.remove(file)
            except OSError:
                pass

```

*Source*: [[`thefuck/rules/dirty_unzip.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_unzip.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/rules/dirty_unzip.py#L45-L58)

### Cleaning Tar Extraction Residue in [`dirty_untar.py`](https://github.com/nvbn/thefuck/blob/main/dirty_untar.py)

Similar to the unzip scenario, the `dirty_untar` rule removes partially extracted files from failed `tar` commands before retrying with the correct directory or extraction options.

```python
def side_effect(old_cmd, command):
    # Remove extracted files before retrying

    import tarfile
    with tarfile.TarFile('archive.tar', 'r') as tar:
        for member in tar.getmembers():
            if member.isfile():
                try:
                    os.remove(member.name)
                except OSError:
                    pass

```

*Source*: [[`thefuck/rules/dirty_untar.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_untar.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/rules/dirty_untar.py#L41-L53)

## Creating a Custom Rule with `side_effect`

To create a rule with cleanup logic, define `side_effect(old_cmd, command)` alongside `match` and `get_new_command`. The function should parse `old_cmd.output` to identify what needs cleaning, perform the operation, and handle errors gracefully.

```python

# thefuck/rules/example_cleanup.py

from thefuck.utils import for_app

@for_app('mycmd')
def match(command):
    return 'ERROR: already exists' in command.output

def get_new_command(command):
    # retry with a force flag

    return f'{command.script} --force'

def side_effect(old_cmd, command):
    # Delete the file that caused the "already exists" error

    import os, re
    m = re.search(r'File: (.+) already exists', old_cmd.output)
    if m:
        filepath = m.group(1)
        if os.path.isfile(filepath):
            os.remove(filepath)

```

When the user runs `$ mycmd project` and sees `ERROR: project already exists (File: ./project)`, thefuck proposes `mycmd project --force`. If the user accepts, `side_effect` removes `./project` before executing the forced command, ensuring the retry succeeds.

## Key Files in the thefuck Repository

| File | Role | Link |
|------|------|------|
| [`thefuck/utils.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/utils.py) | Defines `for_app` decorator and helper utilities used by rules. | [[`utils.py`](https://github.com/nvbn/thefuck/blob/main/utils.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/utils.py) |
| [`thefuck/rules/ssh_known_hosts.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/ssh_known_hosts.py) | Demonstrates side-effect that edits `known_hosts`. | [[`ssh_known_hosts.py`](https://github.com/nvbn/thefuck/blob/main/ssh_known_hosts.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/rules/ssh_known_hosts.py) |
| [`thefuck/rules/dirty_unzip.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_unzip.py) | Side-effect that removes unsafe extracted files before re-unzip. | [[`dirty_unzip.py`](https://github.com/nvbn/thefuck/blob/main/dirty_unzip.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/rules/dirty_unzip.py) |
| [`thefuck/rules/dirty_untar.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_untar.py) | Side-effect that cleans up partially extracted tar files. | [[`dirty_untar.py`](https://github.com/nvbn/thefuck/blob/main/dirty_untar.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/rules/dirty_untar.py) |
| [`thefuck/rules/__init__.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/__init__.py) | Registers all rule modules, enabling the engine to discover optional `side_effect` functions. | [[`__init__.py`](https://github.com/nvbn/thefuck/blob/main/__init__.py)](https://github.com/nvbn/thefuck/blob/master/thefuck/rules/__init__.py) |

These files together illustrate how thefuck leverages the optional `side_effect` function to keep the filesystem in a consistent state, allowing the corrected command to succeed on the very next run.

## Summary

- The `side_effect` function in **nvbn/thefuck** rules performs cleanup or preparation after a user accepts a fix but before the corrected command executes.
- It receives two parameters: `old_cmd` (the original failed command with output) and `command` (the new command), allowing it to parse error details and modify the environment.
- Common use cases include removing invalid SSH host keys from `~/.ssh/known_hosts`, cleaning up partial archive extractions from failed `unzip` or `tar` commands, and deleting lock files that prevent command retries.
- Real-world implementations in [`thefuck/rules/ssh_known_hosts.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/ssh_known_hosts.py), [`thefuck/rules/dirty_unzip.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_unzip.py), and [`thefuck/rules/dirty_untar.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_untar.py) demonstrate parsing regex patterns from `old_cmd.output` to identify and remove specific files or lines.
- When implementing custom rules, `side_effect` should be idempotent, validate paths stay within safe directories, and handle OS errors gracefully to avoid disrupting the user's system.

## Frequently Asked Questions

### When is the `side_effect` function called in the thefuck execution flow?

The `side_effect` function executes after the user confirms a suggested fix but before the corrected command runs. Specifically, the thefuck engine calls `match(command)` to identify the rule, `get_new_command(command)` to generate the fix, prompts the user for confirmation, and then invokes `side_effect(old_cmd, command)` to perform cleanup before finally executing the corrected command.

### What parameters does the `side_effect` function receive?

The function receives two parameters: `old_cmd` and `command`. The `old_cmd` object represents the original failed command and contains attributes like `old_cmd.output` (the stderr/stdout for parsing error details) and `old_cmd.script` (the original command string). The `command` object represents the new corrected command, with `command.script` containing the proposed fix.

### Can a thefuck rule work without a `side_effect` function?

Yes, the `side_effect` function is entirely optional. Most rules in thefuck only implement `match` and `get_new_command`. The engine checks for the existence of `side_effect` using introspection, calling it only if defined. Rules that suggest different command syntax without modifying the filesystem or environment do not require this function.

### How do I ensure my `side_effect` function is safe?

To write a safe `side_effect` function, validate that any file paths you modify stay within the current working directory or other safe zones, use try-except blocks to handle `OSError` exceptions when deleting files, and ensure the operation is idempotent (running it multiple times should not cause errors). Study the reference implementations in [`thefuck/rules/dirty_unzip.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/dirty_unzip.py) and [`thefuck/rules/ssh_known_hosts.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/ssh_known_hosts.py) for patterns on parsing output safely and handling filesystem operations defensively.