{"record":{"id":"5f9698443697ba4b","repo":"Billionmail/BillionMail","slug":"failed-to-get-server-ip-v","errorCode":null,"errorMessage":"Failed to get server IP: %v","messagePattern":"Failed to get server IP: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/domains/domains.go","lineNumber":720,"sourceCode":"\t\tType:  \"TXT\",\n\t\tHost:  \"_dmarc\",\n\t\tValue: fmt.Sprintf(\"v=DMARC1;p=quarantine;rua=mailto:admin@%s\", domain),\n\t}\n\n\tif validateImmediate {\n\t\t// Validate the DMARC record\n\t\trecord.Valid = ValidateTXTRecord(record, domain)\n\t}\n\n\treturn\n}\n\n// GetSPFRecord retrieves the SPF record for a given domain.\nfunc GetSPFRecord(domain string, validateImmediate bool) (record v1.DNSRecord, err error) {\n\tserverIP, err := public.GetServerIP()\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"Failed to get server IP: %v\", err)\n\t\treturn\n\t}\n\n\tipType := \"ip4\"\n\n\tif strings.Contains(serverIP, \":\") {\n\t\tipType = \"ip6\"\n\t}\n\n\t// Format SPF record\n\trecord = v1.DNSRecord{\n\t\tType:  \"TXT\",\n\t\tHost:  \"@\",\n\t\tValue: fmt.Sprintf(\"v=spf1 +a +mx +%s:%s -all\", ipType, serverIP),\n\t}\n\n\tif validateImmediate {\n\t\t// Validate the SPF record","sourceCodeStart":702,"sourceCodeEnd":738,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/domains/domains.go#L702-L738","documentation":"GetSPFRecord fails when public.GetServerIP() cannot determine the server's public IP, which is required to build the SPF record's ip4/ip6 mechanism. The underlying error (network failure, IP-lookup service down, missing outbound connectivity) is wrapped verbatim.","triggerScenarios":"public.GetServerIP() errors — no outbound internet access, the external IP-echo service unreachable or rate-limited, DNS resolution failure, or the host has no default route (common in air-gapped/CI environments).","commonSituations":"Running in an offline/CI container, firewall blocking outbound HTTP(S) used for IP discovery, IPv6-only or NAT'd environments confusing the lookup, or the IP-detection service being temporarily down.","solutions":["Verify outbound connectivity from the server (curl ifconfig.me) and fix firewall/proxy rules","Set a static/explicit server IP in configuration if the environment supports it, bypassing auto-detection","Retry once the IP service is available; consider caching the server IP to reduce dependency","If behind NAT, ensure the detected IP matches the public MX IP or override it manually"],"exampleFix":"// before: hard dependency on live IP lookup\nserverIP, err := public.GetServerIP()\n// after: fall back to configured IP\nip := os.Getenv(\"SERVER_PUBLIC_IP\")\nif ip == \"\" {\n    ip, err = public.GetServerIP()\n}","handlingStrategy":"fallback","validationCode":"if ip := os.Getenv(\"SERVER_PUBLIC_IP\"); ip != \"\" { return nil } // configured override, skip lookup\nif err := net.DialTimeout(\"tcp\", \"1.1.1.1:53\", 3*time.Second); err != nil { return fmt.Errorf(\"no outbound network for IP detection\") }","typeGuard":null,"tryCatchPattern":"serverIP, err := public.GetServerIP()\nif err != nil {\n    log.Printf(\"IP detection failed: %v — using configured override or retrying\", err)\n    serverIP = cfg.PublicIP\n    if serverIP == \"\" { return err }\n}","preventionTips":["Configure a static public IP override for servers behind NAT or in restricted networks","Cache the detected server IP to reduce dependence on external IP-echo services","Monitor outbound connectivity from the mail server"],"tags":["network","spf","ip-lookup","dns"],"backgroundTag":"server-ip-detection-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}