# How Holehe's Update Check Mechanism Works and How to Disable It

> Learn how Holehe's update check queries PyPI and runs pip install --upgrade. Discover how to disable this automatic update mechanism by modifying the source code.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: internals
- Published: 2026-08-15

---

**Holehe automatically checks for updates at startup by querying PyPI and running `pip install --upgrade` if a newer version exists, but this behavior can only be disabled by modifying the source code.**

Holehe, the open-source email OSINT tool by megadose, includes a hard-coded automatic update mechanism that runs before any command-line arguments are parsed. This article explains exactly how this `holehe update check` works, where it's implemented in the codebase, and four methods to disable it when you need consistent, predictable behavior.

## How the Automatic Update Check Works

The update mechanism is implemented entirely in **[`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)** through two key components: the `check_update()` function and its invocation point in `maincore()`.

### Version Retrieval and Comparison

The `check_update()` function fetches the latest package metadata from PyPI using **httpx**:

```python

# From holehe/core.py, lines 34-36

response = httpx.get("https://pypi.org/pypi/holehe/json")
data = response.json()
actual_version = data["info"]["version"]

```

This remote `"version"` field is compared against the locally-defined `__version__` constant (currently `"1.61"` in the analyzed snapshot). The version string is hard-coded at the top of [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) and imported for external use via [`holehe/__init__.py`](https://github.com/megadose/holehe/blob/main/holehe/__init__.py).

### Automatic Upgrade Execution

When versions differ, `check_update()` spawns a subprocess to upgrade the package:

- **Unix-like systems**: Uses `pip3` (lines 69-75)
- **Windows**: Falls back to `pip` (lines 76-81)

After the subprocess completes, the program prints a message and **exits immediately** (lines 85-86). No email reconnaissance occurs until you restart with the updated version.

### Invocation Timing

The check runs unconditionally at program startup. In `maincore()` at **line 97**, you'll find:

```python
check_update()          # <- This executes before argument parsing

args = parser.parse_args()

```

This placement means **no CLI flags exist yet** to disable the behavior—the argument parser hasn't been initialized.

## Can You Disable Holehe's Update Check?

Currently, **there is no built-in CLI option to disable the update check**. The routine is hard-coded without conditional guards. To prevent automatic updates, you must modify [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) using one of these four approaches.

### Method 1: Comment Out the Function Call

The simplest approach is to remove the invocation:

```python

# Original (holehe/core.py ~line 97)

check_update()
args = parser.parse_args()

# Modified—update check disabled

# check_update()

args = parser.parse_args()

```

This stops all version checking with minimal code change.

### Method 2: Replace with No-Op Stub

For a cleaner modification, redefine `check_update()` itself:

```python
def check_update():
    """Disabled update check."""
    pass

```

Place this stub anywhere before line 97, or replace the original function definition (lines 34-86).

### Method 3: Add a `--no-update` CLI Flag

For a proper solution that preserves optional update functionality, extend the argument parser and add conditional logic:

```python

# In ArgumentParser definition (~line 80)

parser.add_argument("--no-update", action="store_true",
                    help="Skip the automatic update check")

# Replace unconditional call with conditional (~line 97)

if not args.no_update:
    check_update()
args = parser.parse_args()

```

This requires editing both the parser setup and the invocation point.

### Method 4: Environment Variable Guard

Modify `check_update()` to respect an environment variable:

```python
def check_update():
    """Check and update holehe if not the last version."""
    import os
    if os.getenv("HOLEHE_NO_UPDATE"):
        return  # Skip update when env var is set

    
    # ... rest of original function unchanged ...

```

Then disable updates per-session:

```bash
export HOLEHE_NO_UPDATE=1
holehe user@example.com

```

## Key Files and Code Locations

| File | Purpose | Relevant Lines |
|------|---------|--------------|
| [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) | Defines `__version__`, `check_update()`, and program entry point | Lines 34-86 (update logic), line 97 (invocation) |
| [`holehe/__init__.py`](https://github.com/megadose/holehe/blob/main/holehe/__init__.py) | Package initialization, version export for external use | Version import from [`core.py`](https://github.com/megadose/holehe/blob/main/core.py) |

All update-related functionality is contained in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py). No other files in the repository reference the update logic, making modifications straightforward.

## Practical Implications

The `holehe update check` behavior has several operational consequences:

- **Network dependency**: Startup fails if PyPI is unreachable (unless you apply a modification)
- **Unexpected exits**: Valid reconnaissance runs may terminate mid-execution for upgrades
- **Environment inconsistency**: Automated pipelines or containerized deployments may experience version drift
- **No audit trail**: The subprocess upgrade happens silently without logging the change

For production OSINT workflows, disabling the automatic update is often essential to ensure reproducible results.

## Summary

- Holehe's `check_update()` in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) queries PyPI at every startup and auto-upgrades via `pip` when versions differ
- The check runs unconditionally at line 97, before CLI argument parsing
- **No built-in flag exists** to disable updates—modification is required
- Four viable approaches: comment the call, stub the function, add `--no-update` flag, or implement environment-variable guard
- All changes are localized to [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)

## Frequently Asked Questions

### Does Holehe require internet access to run?

Yes, both for the update check and for its core OSINT functionality. Even if you disable the update mechanism, Holehe needs network connectivity to query email services. The update check specifically requires access to `https://pypi.org/pypi/holehe/json`.

### Will Holehe lose data if it updates mid-scan?

Holehe exits immediately after upgrading, so any in-progress scan is interrupted. Data from completed modules is lost because the process terminates before results are finalized. Always complete operations before allowing an update, or disable automatic updates for long-running batches.

### Can I pin Holehe to a specific version without modifying code?

Not through official mechanisms. Standard Python approaches like `pip install holehe==1.61` only affect initial installation—the tool still checks for updates and may upgrade itself. Source code modification remains the only reliable method to freeze functionality.

### Is the update check a security risk?

The mechanism itself uses HTTPS to PyPI and subprocesses the standard pip installer, which is relatively benign. However, automatic code execution without user consent violates least-privilege principles in controlled environments. The exit-on-upgrade behavior can also disrupt forensic chain-of-custody for OSINT investigations.