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
- Verify FOFA_EMAIL and FOFA_KEY are set correctly and belong to an account with API access
- Base64-encode the query string exactly as FOFA requires and test the same request with curl
- Check your FOFA account quota and plan level (some query types need paid VIP)
- 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
- Validate FOFA credentials once at startup with a cheap API call before bulk runs
- Always Base64-encode queries exactly as FOFA's API docs specify
- Track FOFA monthly quota and stop before exhaustion
- Surface response.ErrMsg in your own logs — it names the exact cause
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
- request failed with status
- unexpected status code
- unexpected status code
- request failed with status
- error reading response body
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)