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

  1. Log the response body alongside the status code to see LeakIX's actual error message
  2. Check that LEAKIX_API_KEY is set and valid for your LeakIX account
  3. Retry after backoff on 429/5xx; respect LeakIX rate limits
  4. 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

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)