{"record":{"id":"8c73e1d43ec7de29","repo":"projectdiscovery/subfinder","slug":"results-truncated-at-d-subdomains-more-exist","errorCode":null,"errorMessage":"results truncated at %d subdomains, more exist","messagePattern":"results truncated at (.+?) subdomains, more exist","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/subscraping/sources/scanmalware/scanmalware.go","lineNumber":129,"sourceCode":"\t\treturn\n\t}\n\n\tvar body ctDNSResponse\n\terr = jsoniter.NewDecoder(resp.Body).Decode(&body)\n\tif err != nil {\n\t\tresults <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}\n\t\ts.errors++\n\t\ts.closeBody(resp, results)\n\t\treturn\n\t}\n\ts.closeBody(resp, results)\n\n\t// The API caps the list and says so. Report the cap rather than presenting a\n\t// truncated list as complete.\n\tif body.Truncated {\n\t\tresults <- subscraping.Result{\n\t\t\tSource: s.Name(), Type: subscraping.Error,\n\t\t\tError: fmt.Errorf(\"results truncated at %d subdomains, more exist\", subdomainLimit),\n\t\t}\n\t\ts.errors++\n\t}\n\n\ts.emit(ctx, body.Subdomains, session, results)\n}\n\n// enumerateArchive reads hosts present in the public scan archive.\nfunc (s *Source) enumerateArchive(ctx context.Context, domain string, session *subscraping.Session, results chan subscraping.Result) {\n\tfor page := 1; page <= smqlMaxPages; page++ {\n\t\tquery := url.Values{\n\t\t\t\"q\":     {\"domain:*.\" + domain},\n\t\t\t\"limit\": {fmt.Sprint(smqlPageSize)},\n\t\t\t\"page\":  {fmt.Sprint(page)},\n\t\t}\n\t\trequestURL := \"https://scanmalware.com/api/v1/search/smql?\" + query.Encode()\n\n\t\ts.requests++","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/projectdiscovery/subfinder/blob/7a0b91f0fac01b62c65328bd771a3560ae611d6a/pkg/subscraping/sources/scanmalware/scanmalware.go#L111-L147","documentation":"The crt.sh-style CT (certificate transparency) source caps its output at subdomainLimit entries. When the API response reports Truncated, the source emits an Error-type result stating the list was cut short instead of silently returning partial results. It is informational: results before the cap are still valid.","triggerScenarios":"body.Truncated is true in the API response — the domain has more than subdomainLimit matching subdomains in CT logs, so the API cut the list at the limit.","commonSituations":"Enumerating very large domains (e.g. *.github.com, big SaaS hosts) where CT logs contain thousands of entries; wide wildcard certificates producing enormous subdomain sets.","solutions":["Treat as informational — the returned subdomains up to the cap are valid","Narrow the enumeration scope (per-host queries, filters) to stay under the cap","Cross-check with other CT sources or direct brute force for the remaining entries","Increase the limit if your build/config allows and the API supports it"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// caller-side: detect the truncation marker in results\nfor _, r := range results {\n\tif r.Type == subscraping.Error && strings.Contains(r.Error.Error(), \"results truncated\") {\n\t\t// merge additional sources for full coverage\n\t}\n}","typeGuard":"func isTruncatedCTResult(r subscraping.Result) bool {\n\treturn r.Type == subscraping.Error && strings.Contains(r.Error.Error(), \"results truncated at\")\n}","tryCatchPattern":null,"preventionTips":["Don't treat this Error-type result as a hard failure — data before the cap is valid","Cross-validate large domains with multiple sources","Narrow scope for huge CT-heavy domains","Combine with active brute-force to recover entries past the cap"],"tags":["certificate-transparency","truncation","rate-limit"],"backgroundTag":"results-truncated","analyzedSha":"7a0b91f0fac01b62c65328bd771a3560ae611d6a","analyzedAt":"2026-09-06T23:52:02.109Z","contentChangedAt":"2026-09-06T23:52:02.109Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}