Yahoo verification broken three ways: renderer crash (--single-process), stale #username selector, and unsafe false-positive Smtp fallback

Author: iamprabinCreated Sep 16, 2026Updated Sep 16, 2026

Environment

  • Reacher version: 0.11.6 (self-reported at startup; running reacherhq/backend:latest, image digest sha256:463cc36d04eabf44b471b081479d19832a8f8033a20e323aa7716fb176bb1c42)
  • Deployment: Docker, self-hosted, RCH__WORKER__ENABLE=true (RabbitMQ + Postgres worker mode) and standalone /v0/check_email
  • OS inside container: Alpine (bundled alpine-chrome, Chromium 124.0.6367.78)

This report covers three separate, independently-reproducible issues with Yahoo verification, found while debugging why every Yahoo check was returning is_reachable: "unknown".


1. Headless method crashes the renderer via hardcoded --single-process --no-zygote

core/src/smtp/headless.rs hardcodes these Chrome args:

rust
let mut opts = serde_json::json!({
    "args": [
        "--headless=new", "--disable-gpu", "--disable-dev-shm-usage", "--no-sandbox",
        "--use-gl=angle", "--use-angle=swiftshader",
        "--single-process", "--no-zygote",
        "--window-size=800x600", "--disable-extensions", "--disable-software-rasterizer",
        "--disable-dev-shm-usage", "--disable-background-networking",
        "--js-flags=\"--max-old-space-size=256\"",
    ]
});

--single-process combined with --no-zygote is a well-documented unstable combination for headless Chrome (the browser and renderer share one process, so any renderer-side fault takes down the whole session with no recovery path). In our environment this reliably crashed with:

[SEVERE]: Unable to receive message from renderer

surfaced to the API as:

json
{"error": {"type": "HeadlessError", "message": {"Cmd": "NotW3C(\"disconnected\")"}}}

This matches #1598 and #1560, which report the identical symptom on different infrastructure (a different VPS provider in #1598's case) with no diagnosis in either thread.

Reproduction / verification of root cause: created a raw WebDriver session directly against the same chromedriver instance, deliberately without --single-process/--no-zygote:

bash
curl -X POST http://localhost:9515/session -H "Content-Type: application/json" -d '{
  "capabilities": {"alwaysMatch": {"browserName": "chrome",
    "goog:chromeOptions": {"args": ["--headless=new","--no-sandbox","--disable-dev-shm-usage","--disable-gpu"]}}}}'

That session navigated to https://login.yahoo.com/forgot and rendered normally (title came back correctly, no crash). Reintroducing either flag reproduces the crash consistently.

Suggested fix: drop --single-process --no-zygote from the hardcoded args in headless.rs, or make them configurable (e.g. via RCH__WEBDRIVER__CHROME_ARGS or similar) so self-hosters aren't stuck with a fixed unstable flag set.


2. Yahoo changed their recovery-page field ID (#username#emailInput), breaking the Headless selector

Once the crash above is worked around, Headless no longer crashes but instead times out on every single check:

json
{"error": {"type": "HeadlessError", "message": {"Cmd": "timeout waiting on condition: "}}}

core/src/smtp/yahoo/headless.rs fills in #username on https://login.yahoo.com/forgot and waits for one of five selectors (.error-msg, .ctx-account_is_locked, .recaptcha-challenge, #email-verify-challenge, #challenge-selector-challenge).

Reproduction: navigated to https://login.yahoo.com/forgot via a raw WebDriver session and inspected the page source - the actual input field currently has id="emailInput", not id="username". Chromedriver's POST /session/{id}/element for {"using":"css selector","value":"#username"} returns 404 Not Found - the element genuinely doesn't exist on the page anymore, so the fill step (and everything downstream) can never succeed, and the wait loop runs out the clock every time regardless of whether the target email exists or not.

Suggested fix: update the selector in yahoo/headless.rs from #username to #emailInput (and it's worth double-checking the five outcome selectors are still current too, since Yahoo appears to have redesigned this page).


3. Smtp method produces false positives for Yahoo - catch-all detection doesn't catch it

Not a crash, but a correctness issue: with overrides.yahoo.type = "smtp", Yahoo's MX servers accept RCPT TO for any address, including ones that obviously don't exist:

bash
curl -X POST /v0/check_email -d '{"to_email":"2133214sdlfksdflksjdflkdwerweoriu29842734234sodfjlfkjsd@yahoo.com","yahoo_verif_method":"Smtp"}'
# -> is_reachable: "safe", smtp.is_deliverable: true, smtp.is_catch_all: false

A real address returns the identical shape of result. Since is_catch_all is reported false for both, the existing catch-all detection isn't identifying this domain as catch-all-like behavior, when empirically it behaves exactly like one (accept-all at SMTP time, presumably filtered/bounced downstream, which is a known anti-harvesting pattern some large providers use). Right now, defaulting Yahoo to Smtp would silently mark every fake Yahoo address in a bulk verification run as "safe" - actively worse than an honest "unknown".

Suggested fix: either have the existing catch-all probe run against yahoo.com specifically and correctly detect this pattern (if the probe technique itself doesn't trigger the same accept-everything behavior it's meant to detect, worth investigating why), or document that Smtp is not a safe verification method for Yahoo at all until that's resolved.


Current workaround we're using

Given all three, the least-bad option today is overrides.yahoo.type = "api". It doesn't succeed (YahooError: NoSessionIndex on every call - possibly a fourth, related issue with Yahoo's password-recovery API flow, not investigated further here), but it fails fast (~0.7-0.9s) and consistently, honestly returning unknown with no false positives - a much better default than Headless's ~90s timeout for the same non-answer, and safer than Smtp's false "safe" results.

Source: reacherhq/check-if-email-exists