How Holehe Handles Module Exceptions and Errors Gracefully
Holehe captures all module failures in the launch_module coroutine within holehe/core.py, converting exceptions into standardized error dictionaries that allow the scan to continue while alerting users via colored console output.
When investigating email addresses across hundreds of platforms, a single broken site or network timeout could derail an entire OSINT operation. The megadose/holehe repository implements a robust fault-tolerance mechanism that isolates individual module failures, ensuring that one problematic site never aborts the complete search workflow.
The Central Exception Handler in launch_module
The core resilience logic resides in the launch_module async function defined in holehe/core.py (lines 66-78). This coroutine wraps every site-specific check in a broad exception trap that intercepts any runtime failure—from network timeouts to parsing errors.
async def launch_module(module, email, client, out):
try:
await module(email, client, out) # Normal execution
except Exception: # Catch any unexpected error
name = str(module).split('<function ')[1].split(' ')[0]
out.append({
"name": name,
"domain": data[name],
"rateLimit": False,
"error": True, # Flag the entry as an error
"exists": False,
"emailrecovery": None,
"phoneNumber": None,
"others": None
})
When a module raises an exception, launch_module immediately constructs a standardized result dictionary with error: True and appends it to the shared out list. The dictionary preserves the module name and associated domain from the data mapping, ensuring forensic traceability even when the underlying function crashes.
Visual Error Reporting in print_result
After collection completes, the print_result function (lines 26-32 in holehe/core.py) renders error states to the console with distinctive red formatting. This visual feedback distinguishes failed checks from negative results or successful findings.
elif "error" in results.keys() and results["error"] and args.onlyused == False:
toprint = ""
if results["others"] is not None and "Message" in str(results["others"].keys()):
toprint = " Error message: " + results["others"]["errorMessage"]
websiteprint = print_color("[!] " + results["domain"] + toprint,
"red", args)
print(websiteprint)
The red [!] prefix immediately alerts investigators that a specific domain could not be checked, while optional embedded error messages provide additional diagnostic context when available.
Why Graceful Degradation Matters for OSINT Tools
Holehe executes site checks concurrently using trio.open_nursery() or asyncio gather patterns. Without centralized exception handling in launch_module, a single malformed HTTP response or unexpected HTML structure would propagate upward and crash the entire orchestration layer.
By funneling every module through the same try/except pathway, the tool achieves fault isolation: successful modules return normally, failed modules return error dictionaries, and the aggregate results list always contains a deterministic entry for every queried site.
Practical Implementation Example
The following snippet demonstrates the internal workflow, showing how exceptions automatically generate error-flagged entries without stopping execution:
import asyncio, httpx
from holehe.core import import_submodules, get_functions, launch_module
async def demo(email):
modules = import_submodules("holehe.modules")
sites = get_functions(modules)
client = httpx.AsyncClient(timeout=10)
out = []
async with trio.open_nursery() as n:
for site in sites:
n.start_soon(launch_module, site, email, client, out)
await client.aclose()
# Process results: error entries coexist with successful checks
for r in out:
if r["error"]:
print(f"⚠️ {r['domain']} failed")
else:
status = "found" if r["exists"] else "not found"
print(f"✅ {r['domain']} → {status}")
asyncio.run(demo("test@example.com"))
In this pattern, the out list contains mixed results: standard dictionaries for successful lookups and error-denoted dictionaries for failures, preserving complete audit trails.
Summary
- Centralized trapping: The
launch_modulecoroutine inholehe/core.pywraps every module call in atry/exceptblock that catches all exceptions. - Structured error objects: Failed modules return dictionaries with
error: True,exists: False, and domain metadata rather than crashing. - Visual distinction: The
print_resultfunction renders errors in red with a[!]prefix, making failures immediately recognizable in console output. - Scan continuity: Because exceptions are converted to data rather than propagated, parallel execution continues uninterrupted even when individual sites malfunction.
Frequently Asked Questions
How does holehe prevent one module failure from crashing the entire scan?
Holehe runs each site-specific module inside the launch_module coroutine, which implements a broad except Exception clause. When any module raises an error—whether from network timeouts, parsing failures, or unexpected HTTP responses—the exception is caught and converted into a structured error dictionary. This prevents the exception from bubbling up to the main async nursery or event loop, allowing other concurrent modules to complete normally.
What information is included when a module reports an error?
Error entries contain the module's function name, the target domain, boolean flags indicating rateLimit: False and error: True, and null values for recovery fields. If the module populated the others dictionary with an error message before failing, that message is preserved and displayed in the final console output alongside the red [!] indicator.
How can developers identify which specific modules failed during a scan?
Each error dictionary includes the "name" key derived from the module's function name and the "domain" key mapped from the internal data dictionary. When reviewing results programmatically, developers can filter the output list for entries where result["error"] is True to isolate problematic sites. The console output also prints the domain name in red, providing immediate visual identification of failed checks.
Does holehe differentiate between rate limits and other types of errors?
Yes, the result schema includes a dedicated rateLimit boolean that operates independently from the error flag. While the launch_module exception handler sets error: True for any uncaught exception, modules that detect HTTP 429 responses or rate-limiting headers typically set rateLimit: True through their normal execution path rather than raising exceptions. This distinction allows investigators to distinguish between temporary blocking and technical failures.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →