projectdiscovery/subfinder · error

unexpected status code

Error message

unexpected status code %d received from %s

What it means

The shodanct (Shodan Certificate Transparency) source raises this when the HTTP response status is anything other than 200 OK from the searchURL. The library includes the actual status code and URL so the caller can see exactly which endpoint rejected the request.

Solutions

  1. Log/inspect resp.StatusCode to identify the class of failure (auth vs rate limit vs server error).
  2. Ensure a valid Shodan API key is configured for the shodanct source.
  3. Add backoff/retry for 429/5xx responses and reduce request frequency.
Defensive patterns

Strategy: retry

Validate before calling

if os.Getenv("SHODAN_API_KEY") == "" { log.Warn("shodanct will fail with non-200 status") }

Try / catch

for r := range results {
  if r.Type == subscraping.Error {
    var code int
    if n, _ := fmt.Sscanf(r.Error.Error(), "unexpected status code %d", &code); n == 1 && (code == 429 || code >= 500) {
      // retry with exponential backoff
    }
  }
}

Prevention

When it happens

Trigger: GET to the Shodan CT search URL returns a non-200 status: 401/403 for bad or missing API key, 429 for rate limiting, 5xx for server-side errors.

Common situations: Missing SHODANCT_API_KEY or wrong plan tier; invoking the source frequently and hitting rate limits; transient Shodan outages.

Related errors


AI-assisted analysis of projectdiscovery/subfinder@7a0b91f0fa (2026-09-06). Data as JSON: /api/errors/cfcb4ba28d260524. Report an issue: GitHub.

Appendix: source

Thrown at pkg/subscraping/sources/shodanct/shodanct.go:49

		defer func(startTime time.Time) {
			s.timeTaken = time.Since(startTime)
			close(results)
		}(time.Now())

		searchURL := fmt.Sprintf("https://ctl.shodan.io/api/v1/domain/%s/hostnames", domain)
		s.requests++
		resp, err := session.SimpleGet(ctx, searchURL)
		if err != nil {
			results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
			s.errors++
			session.DiscardHTTPResponse(resp)
			return
		}

		if resp.StatusCode != http.StatusOK {
			results <- subscraping.Result{
				Source: s.Name(), Type: subscraping.Error,
				Error: fmt.Errorf("unexpected status code %d received from %s", resp.StatusCode, searchURL),
			}
			s.errors++
			session.DiscardHTTPResponse(resp)
			return
		}

		defer session.DiscardHTTPResponse(resp)

		var hostnames []string
		if err := jsoniter.NewDecoder(resp.Body).Decode(&hostnames); err != nil {
			results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
			s.errors++
			return
		}

		for _, hostname := range hostnames {
			for _, subdomain := range session.Extractor.Extract(hostname) {
				select {

View on GitHub (pinned to 7a0b91f0fa)