# How holehe Handles Errors in Individual Modules: A Deep Dive into Two-Layer Exception Management

> holehe handles errors at module and core levels with two-layer exception management. It standardizes exceptions for uninterrupted scans. Learn how.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: deep-dive
- Published: 2026-08-18

---

**`holehe` catches errors at both the module and core levels, converting all exceptions into standardized dictionary records that let the scan continue uninterrupted.**

The `holehe` tool, developed by [@megadose](https://github.com/megadose), checks email addresses against dozens of online services through async Python modules. Since any single service can fail without invalidating the entire search, the codebase implements a robust **two-layer error handling strategy** that guarantees every module contributes a usable result—whether successful or not.

## The Two-Layer Error Handling Architecture

`holehe` organizes error handling into distinct module-level and core-level responsibilities. This ensures graceful degradation when services rate-limit requests or experience outages.

### Module-Level: Service-Specific Exception Catching

Each service module wraps its HTTP logic in a `try … except Exception:` block. This gives module authors control over interpreting specific response patterns—distinguishing, for example, between a genuine "email exists" response and a throttling rejection.

In [`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py) (lines 30-36), the Twitter module implements this pattern:

```python

# holehe/modules/social_media/twitter.py

async def twitter(email, client, out):
    name, domain, method = "twitter", "twitter.com", "register"
    try:
        req = await client.get("https://api.twitter.com/i/users/email_available.json",
                               params={"email": email})
        # … normal success handling …

    except Exception:                     # ← module-level catch

        out.append({
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": False,
            "rateLimit": True,            # service signaled throttling

            "error": True,                # we hit an exception

            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        })

```

The module author chooses to set `rateLimit=True` because most Twitter failures stem from request throttling. This semantic context would be lost at a generic handler.

### Core-Level: Universal Safety Net

The `launch_module` function in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) (lines 66-78) provides a fallback wrapper. It catches any exception that escapes the module's own handling—whether from coding errors, unexpected API changes, or network interruptions:

```python

# holehe/core.py – generic launcher

async def launch_module(module, email, client, out):
    try:
        await module(email, client, out)   # run the service-specific coroutine

    except Exception:                     # ← core-level safety net

        name = str(module).split('<function ')[1].split(' ')[0]
        out.append({
            "name": name,
            "domain": data[name],
            "rateLimit": True,
            "error": True,
            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        })

```

This guarantees the `out` list receives a properly formed entry even for completely broken modules, preventing the entire scan from crashing.

## The Standardized Error Record Format

Both layers produce **identical dictionary structures**, enabling uniform downstream processing. Every result contains these keys:

| Key | Purpose | Typical Error Value |
|-----|---------|-------------------|
| `name` | Service identifier | `"twitter"` |
| `domain` | Service domain | `"twitter.com"` |
| `method` | Lookup technique | `"register"` or omitted |
| `frequent_rate_limit` | Persistent throttling flag | `False` |
| `rateLimit` | Request was throttled | `True` |
| `error` | Exception was caught | `True` |
| `exists` | Email confirmed on service | `False` (unknown due to error) |
| `emailrecovery` | Recovery email leaked | `None` |
| `phoneNumber` | Phone leaked | `None` |
| `others` | Miscellaneous data | `None` |

The `exists=False` field is critical: it prevents error states from being misinterpreted as positive matches. Meanwhile, `error=True` triggers red `[!]` markers in the CLI output.

## Visual Feedback in the Command Line Interface

When you execute `holehe`, the standardized records render as intuitive status indicators:

```bash
$ holehe example@example.com
[!] twitter      # ← rate-limit or exception caught (error=True)

[-] instagram    # ← email not found on service

[+] github       # ← email found (exists=True)

...

```

The `print_result` function in [`core.py`](https://github.com/megadose/holehe/blob/main/core.py) consumes these dictionaries without needing service-specific logic—direct proof of how the uniform schema simplifies the architecture.

## Design Rationale: Why Two Layers?

**Module-level handling** preserves semantic nuance. A developer can inspect HTTP status codes, JSON error fields, or response headers to classify failures precisely.

**Core-level handling** provides defense in depth. Even if a module lacks proper exception wrapping—perhaps a new contributor's first submission—the orchestration layer prevents cascade failures.

This separation follows the **fail-fast, fail-safe** principle: modules should handle expected failures explicitly, but the system survives unexpected ones regardless.

## Summary

- **`holehe` handles errors in individual modules through a two-layer architecture**: service-specific catching plus a universal core wrapper.
- **All errors convert to standardized dictionaries** with `error=True`, `rateLimit=True`, and `exists=False`, ensuring consistent downstream processing.
- **Module-level handlers** in files like [`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py) preserve contextual interpretation of API responses.
- **The core launcher** at [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) lines 66-78 guarantees scan continuity even against completely unhandled exceptions.
- **Uniform dictionary schemas** eliminate special-case logic in the UI layer, as implemented in `print_result`.

## Frequently Asked Questions

### What happens if a holehe module forgets to wrap its HTTP request in try/except?

The core-level `launch_module` function in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) catches the escaping exception and produces a standard error record anyway. The scan continues normally, displaying `[!]` for that service.

### Why does almost every holehe error set rateLimit=True?

Most module authors treat exceptions as throttling indicators because rate-limiting is the dominant failure mode for aggressive email enumeration. The core launcher follows this convention for consistency.

### Can I customize error handling for a specific service module?

Yes. The module-level `try … except` block in each service file allows custom logic—for instance, parsing specific error codes from the target API before falling back to the generic error record format.

### Where does holehe display the error status to users?

The `print_result` function in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) renders dictionary records as CLI symbols: `[!]` for errors, `[-]` for confirmed non-existence, and `[+]` for successful email matches.