{"record":{"id":"a05af610a4a8ca3d","repo":"Billionmail/BillionMail","slug":"no-spf-record-found-from-cache","errorCode":null,"errorMessage":"No SPF record found (from cache)","messagePattern":"No SPF record found \\(from cache\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/relay/relay_v1_list_relay_configs.go","lineNumber":181,"sourceCode":"\tif host != \"\" {\n\t\tinclude := \"include:\" + host\n\t\treturn strings.Contains(spf, include)\n\t}\n\treturn false\n}\n\nvar (\n\tspfCache = gcache.New()\n\tmu       = &sync.Mutex{}\n)\n\nfunc GetRealSPFRecord(ctx context.Context, domain string) (string, error) {\n\tcacheKey := fmt.Sprintf(\"real_spf_record:%s\", domain)\n\n\tif value, _ := spfCache.Get(ctx, cacheKey); value != nil {\n\n\t\tif value.String() == \"not_found\" {\n\t\t\treturn \"\", fmt.Errorf(\"No SPF record found (from cache)\")\n\t\t}\n\t\treturn value.String(), nil\n\t}\n\n\tmu.Lock()\n\tdefer mu.Unlock()\n\n\tif value, _ := spfCache.Get(ctx, cacheKey); value != nil {\n\t\tif value.String() == \"not_found\" {\n\t\t\treturn \"\", fmt.Errorf(\"No SPF record found (from cache)\")\n\t\t}\n\t\treturn value.String(), nil\n\t}\n\n\tvar spf string\n\tvar finalErr error\n\ttxts, err := net.LookupTXT(domain)\n\tif err != nil {","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/relay/relay_v1_list_relay_configs.go#L163-L199","documentation":"GetRealSPFRecord looks up the domain's SPF (v=spf1 TXT) record and caches results; a previous lookup for this domain already failed and stored the sentinel 'not_found' value. When the cache is hit, the function returns this cached negative result instead of re-querying DNS. It is a cached version of error [45]/[48].","triggerScenarios":"Calling ListRelayConfigs (which resolves SPF) for a domain whose earlier DNS TXT lookup produced no v=spf1 record within the last 2 minutes, so spfCache holds 'not_found'.","commonSituations":"Domain genuinely has no SPF record (never configured); querying the wrong domain (e.g. MX-less subdomain); negative cache masking a transient DNS outage that happened 2 minutes ago.","solutions":["Verify with 'dig TXT yourdomain.com' that an spf1 record exists; add one if missing (v=spf1 ...)","Wait 2 minutes for the negative cache entry to expire and retry","Confirm you are querying the sending domain, not a subdomain","Clear the app cache or restart if you need an immediate re-check"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// check SPF presence out-of-band before relying on the API\ndig +short TXT example.com | grep -q 'v=spf1'","typeGuard":null,"tryCatchPattern":"try {\n  spf = await api.getRealSPFRecord(domain);\n} catch (e) {\n  if (e.message.includes('No SPF record')) {\n    spf = null; // proceed without SPF, flag domain for setup\n    markDomainNeedsSPF(domain);\n  }\n}","preventionTips":["Publish SPF records for all sending domains","Remember the 2-minute negative cache when retesting","Query the exact envelope-sender domain","Monitor DNS for all relay domains"],"tags":["dns","spf","cache","negative-caching"],"backgroundTag":"spf-record-missing","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}