# How Holehe Handles Exceptions Within Modules: Dual-Layer Error Management

> Discover how Holehe handles exceptions within modules using its dual-layer error management system. Ensure uninterrupted OSINT scans with robust local and central exception catching.

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

---

**Holehe implements a robust two-layer exception handling system where individual async modules wrap their request logic in try-except blocks to isolate failures locally, while the central `launch_module` dispatcher in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) provides a safety net to catch any uncaught exceptions and ensure the OSINT scan continues uninterrupted across all 200+ services.**

Holehe is an open-source OSINT (Open Source Intelligence) tool designed to check if an email address is registered on various websites. Because the tool queries over 200 different services—each with unique APIs, rate limits, and potential points of failure—its modular architecture must gracefully handle network errors, unexpected responses, and rate limiting without crashing the entire operation.

## The Two-Layer Exception Handling Architecture

The repository `megadose/holehe` employs a defensive programming strategy that nests exception handling at two distinct levels. This design ensures that even if a single service module encounters a catastrophic error, the overall email investigation continues processing remaining services.

### Module-Level Exception Isolation

Every service module in Holehe follows a standardized async function signature accepting `email`, `client`, and `out` parameters. Each module wraps its entire HTTP request logic within a broad `try … except Exception` block to catch anything from network timeouts to JSON parsing errors.

When an exception occurs at this level, the module appends a standardized result dictionary to the shared `out` list, typically marking `rateLimit` as `True` to indicate a temporary failure, while keeping `error` as `False` since the failure was handled gracefully.

### Central Dispatcher Safety Net

Even with module-level protection, the central orchestrator in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) implements an additional safeguard. The `launch_module` coroutine wraps every module call in its own `try … except Exception` clause, ensuring that programming errors or overlooked edge cases never terminate the main event loop.

## Exception Handling Pattern in Individual Modules

Examining the Twitter module reveals the exact pattern replicated across the codebase. Located at [`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py), this module demonstrates how to handle both successful responses and failures:

```python

# holehe/modules/social_media/twitter.py

async def twitter(email, client, out):
    name = "twitter"
    domain = "twitter.com"
    method = "register"
    frequent_rate_limit = False

    try:
        req = await client.get(
            "https://api.twitter.com/i/users/email_available.json",
            params={"email": email})
        if req.json()["taken"]:
            out.append({
                "name": name, "domain": domain, "method": method,
                "frequent_rate_limit": frequent_rate_limit,
                "rateLimit": False, "exists": True,
                "emailrecovery": None, "phoneNumber": None, "others": None,
            })
        else:
            out.append({
                "name": name, "domain": domain, "method": method,
                "frequent_rate_limit": frequent_rate_limit,
                "rateLimit": False, "exists": False,
                "emailrecovery": None, "phoneNumber": None, "others": None,
            })
    except Exception:                     # ← module‑level catch

        out.append({
            "name": name, "domain": domain, "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": True, "exists": False,
            "emailrecovery": None, "phoneNumber": None, "others": None,
        })

```

In this implementation, any exception—whether a connection timeout, HTTP 5xx error, or unexpected JSON structure—triggers the except block. The module then reports `rateLimit=True` to signal that the check could not complete, allowing the main loop to continue with other services.

## The Central Fallback in holehe/core.py

The `launch_module` function in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) serves as the ultimate backup for exception handling. When a module fails to catch an exception internally (perhaps due to a programming error or unexpected API change), this dispatcher catches the error and generates a generic failure record:

```python

# holehe/core.py – central dispatcher

async def launch_module(module, email, client, out):
    try:
        await module(email, client, out)               # ← module runs its own try/except

    except Exception:                                 # ← fallback safety‑net

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

```

This safety net distinguishes between handled module failures (marked as rate limits) and unhandled exceptions (marked as errors) by setting `"error": True` in the output dictionary.

## Result Dictionary Structure and Error Classification

Holehe standardizes all module communication through a consistent dictionary schema appended to the `out` list. This uniformity allows the main reporting logic to process results without knowing specific module details.

The dictionary includes specific boolean flags for different failure states:

- **`exists`**: Indicates whether the email is registered on the service
- **`rateLimit`**: Set to `True` when the module catches an exception (typically treated as rate limiting or temporary unavailability)
- **`error`**: Set to `True` only when the central `launch_module` catches an uncaught exception

This dual-flag system allows users to distinguish between "service temporarily unavailable" (rate limit) and "unexpected failure occurred" (error).

## Creating Custom Modules with Proper Exception Handling

When developing new modules for Holehe, developers should follow the established exception handling pattern to maintain system stability. A template for a custom module should include the same defensive structure:

```python

# mymodule.py

async def myservice(email, client, out):
    name, domain, method = "myservice", "myservice.com", "register"
    try:
        resp = await client.post("https://api.myservice.com/check", json={"email": email})
        exists = resp.json()["registered"]
        out.append({"name": name, "domain": domain, "method": method,
                    "rateLimit": False, "exists": exists,
                    "emailrecovery": None, "phoneNumber": None, "others": None})
    except Exception:                     # any unexpected error → safe‑guard

        out.append({"name": name, "domain": domain, "method": method,
                    "rateLimit": True, "exists": False,
                    "emailrecovery": None, "phoneNumber": None,
                    "others": None})

```

This pattern ensures that your module contributes to the overall scan reliability rather than introducing crash points.

## Summary

- **Holehe uses nested exception handling**: Individual modules catch their own errors, while [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) provides a final safety net through `launch_module`.
- **Module-level try-except blocks**: Every service module wraps HTTP requests in `try … except Exception` to catch network failures, parsing errors, and API anomalies.
- **Standardized error reporting**: Modules append uniform dictionaries with `rateLimit` and `error` flags to the shared `out` parameter, enabling consistent result processing.
- **Graceful degradation**: When exceptions occur, modules mark `rateLimit=True` and continue execution, ensuring one failed service doesn't crash the entire OSINT investigation.
- **Central dispatcher backup**: The `launch_module` function catches any uncaught exceptions and records them with `"error": True`, distinguishing between handled and unhandled failures.

## Frequently Asked Questions

### What happens if a Holehe module forgets to catch an exception?

If a module fails to include a try-except block, the central `launch_module` dispatcher in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) catches the exception automatically. It extracts the module name from the function object using string parsing—`str(module).split('<function ')[1].split(' ')[0]`—and appends an error entry with `"error": True` and `"rateLimit": False` to the output list, allowing the scan to continue with remaining services.

### Why does Holehe mark exceptions as rate limits rather than errors?

Modules typically set `rateLimit=True` when catching exceptions because most failures (timeouts, connection refused, HTTP 5xx) represent temporary service unavailability rather than permanent API changes. This distinction allows users to retry scans later, whereas `"error": True` (set only by the central dispatcher) indicates an unexpected code failure requiring developer investigation.

### How does the launch_module function extract the service name from a module?

The `launch_module` function uses string manipulation on the module's string representation to extract the function name: `str(module).split('<function ')[1].split(' ')[0]`. This approach retrieves the module identifier without requiring additional metadata parameters, enabling the generic error handler to label results correctly regardless of which service failed.

### Can I customize exception handling for specific error types in my module?

Yes, you can implement specific exception handling before the general `except Exception` block. For example, you might catch `httpx.TimeoutException` separately to set longer retry flags, or catch `json.JSONDecodeError` to handle malformed API responses differently. However, you should always maintain the final broad `except Exception` block to ensure no error propagates to the central dispatcher.