projectdiscovery/subfinder · error

%s

Error message

%s

What it means

The FOFA source in subscraping received an HTTP 200 response whose JSON body flagged an application-level error (response.Error == true). The library surfaces the API's own ErrMsg field verbatim as a Go error and pushes it on the results channel, then stops enumeration for this source. It means the FOFA API rejected the query even though the HTTP request itself succeeded.

Solutions

  1. Verify FOFA_EMAIL and FOFA_KEY are set correctly and belong to an account with API access
  2. Base64-encode the query string exactly as FOFA requires and test the same request with curl
  3. Check your FOFA account quota and plan level (some query types need paid VIP)
  4. Read the ErrMsg returned in this error — it contains FOFA's own explanation of the rejection

Example fix

// before
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("%s", response.ErrMsg)}
// after
if response.ErrMsg == "" {
    response.ErrMsg = "unknown FOFA API error"
}
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("fofa api error: %s", response.ErrMsg)}
Defensive patterns

Strategy: validation

Validate before calling

email := os.Getenv("FOFA_EMAIL")
key := os.Getenv("FOFA_KEY")
if email == "" || key == "" {
    return fmt.Errorf("FOFA_EMAIL and FOFA_KEY must be set with a valid FOFA API account")
}
if _, err := base64.StdEncoding.DecodeString(query); err != nil {
    return fmt.Errorf("FOFA query must be Base64-encoded: %w", err)
}

Try / catch

for res := range src.Fetch(ctx, domain) {
    if res.Type == subscraping.Error {
        if strings.Contains(res.Error.Error(), "account") || strings.Contains(res.Error.Error(), "VIP") {
            // credentials/quota problem — skip FOFA, do not retry
            continue
        }
        log.Printf("fofa: %v", res.Error)
    }
}

Prevention

When it happens

Trigger: Any FOFA API call where the decoded response has error=true, with response.ErrMsg carrying the API's message — e.g. invalid API email/key pair, malformed or non-Base64 query string, query violating FOFA syntax, or account quota/privilege limits (FOFA 'personal' plan API restrictions).

Common situations: Expired or wrong FOFA API credentials (FOFA_EMAIL/FOFA_KEY), passing a raw query that is not Base64-encoded, using query syntax not allowed on the user's FOFA tier, or hitting the monthly API quota (FOFA returns 'VIP query limit' style messages).

Related errors


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

Appendix: source

Thrown at pkg/subscraping/sources/fofa/fofa.go:81

			results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
			s.errors++
			session.DiscardHTTPResponse(resp)
			return
		}

		var response fofaResponse
		err = jsoniter.NewDecoder(resp.Body).Decode(&response)
		if err != nil {
			results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
			s.errors++
			session.DiscardHTTPResponse(resp)
			return
		}
		session.DiscardHTTPResponse(resp)

		if response.Error {
			results <- subscraping.Result{
				Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("%s", response.ErrMsg),
			}
			s.errors++
			return
		}

		if response.Size > 0 {
			for _, subdomain := range response.Results {
				select {
				case <-ctx.Done():
					return
				default:
				}
				if strings.HasPrefix(strings.ToLower(subdomain), "http://") || strings.HasPrefix(strings.ToLower(subdomain), "https://") {
					subdomain = subdomain[strings.Index(subdomain, "//")+2:]
				}
				re := regexp.MustCompile(`:\d+$`)
				if re.MatchString(subdomain) {
					subdomain = re.ReplaceAllString(subdomain, "")

View on GitHub (pinned to 7a0b91f0fa)