projectdiscovery/subfinder · error
request failed with status
Error message
request failed with status %d
What it means
The LeakIX source requires HTTP 200 and treats every other status code as a hard failure: when resp.StatusCode != 200 it emits this error on the results channel and stops. It is a plain transport-level rejection by the LeakIX API (auth, rate limit, server error) with only the numeric status included — the response body is discarded.
Solutions
- Log the response body alongside the status code to see LeakIX's actual error message
- Check that LEAKIX_API_KEY is set and valid for your LeakIX account
- Retry after backoff on 429/5xx; respect LeakIX rate limits
- Check the LeakIX status page / API changelog for outages or endpoint changes
Example fix
// before
if resp.StatusCode != 200 {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("request failed with status %d", resp.StatusCode)}
s.errors++
return
}
// after
if resp.StatusCode != 200 {
body, _ := io.ReadAll(resp.Body)
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error,
Error: fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(body))}
s.errors++
return
} Defensive patterns
Strategy: retry
Validate before calling
if os.Getenv("LEAKIX_API_KEY") == "" {
log.Println("warning: LEAKIX_API_KEY not set; leakix requests may return 401/403")
} Try / catch
for res := range src.Fetch(ctx, domain) {
if res.Type == subscraping.Error {
var statusErr interface{ Error() string }
msg := res.Error.Error()
if strings.Contains(msg, "status 429") {
time.Sleep(backoff) // rate limited — retry later
} else if strings.Contains(msg, "status 5") {
// transient server error — retry with backoff
} else {
log.Printf("leakix: %v (check LEAKIX_API_KEY)", res.Error)
}
}
} Prevention
- Set a valid LEAKIX_API_KEY before running enumeration
- Throttle request rate to stay under LeakIX limits and back off on 429
- Log full status codes and retry only transient classes (429, 5xx)
- Watch LeakIX API status/changelog for endpoint or auth changes
When it happens
Trigger: Any request to the LeakIX API that returns a non-200 status: missing/invalid LEAKIX_API_KEY (401/403), rate limiting (429), or server-side errors (5xx) during Fetch.
Common situations: Running without a LeakIX API key (anonymous tier heavily limited / 401), an expired or revoked key, aggressive enumeration triggering 429, or LeakIX API downtime returning 5xx.
Related errors
AI-assisted analysis of projectdiscovery/subfinder@7a0b91f0fa (2026-09-06).
Data as JSON: /api/errors/b1e58f51150b1654.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/subscraping/sources/leakix/leakix.go:56
}
// Pick an API key
randomApiKey := subscraping.PickRandom(s.apiKeys, s.Name())
if randomApiKey != "" {
headers["api-key"] = randomApiKey
}
// Request
s.requests++
resp, err := session.Get(ctx, "https://leakix.net/api/subdomains/"+domain, "", headers)
if err != nil {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
s.errors++
return
}
defer session.DiscardHTTPResponse(resp)
if resp.StatusCode != 200 {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("request failed with status %d", resp.StatusCode)}
s.errors++
return
}
// Parse and return results
var subdomains []subResponse
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&subdomains)
if err != nil {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
s.errors++
return
}
for _, result := range subdomains {
select {
case <-ctx.Done():
return
case results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: result.Subdomain}:
s.results++View on GitHub (pinned to 7a0b91f0fa)