{"record":{"id":"1f34a7adc0b43b6b","repo":"chenhg5/cc-connect","slug":"remote-image-host-resolved-to-no-usable-ips","errorCode":null,"errorMessage":"remote image host resolved to no usable IPs","messagePattern":"remote image host resolved to no usable IPs","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"platform/feishu/feishu.go","lineNumber":6675,"sourceCode":"\t\t\tips = append(ips, addr.IP)\n\t\t}\n\t}\n\n\tvar firstBlocked net.IP\n\tfor _, ip := range ips {\n\t\tif isBlockedRichCardImageIP(ip) {\n\t\t\tif firstBlocked == nil {\n\t\t\t\tfirstBlocked = ip\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tdialer := &net.Dialer{Timeout: richCardImageFinalWait}\n\t\treturn dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port))\n\t}\n\tif firstBlocked != nil {\n\t\treturn nil, fmt.Errorf(\"remote image host resolved to blocked IP %s\", firstBlocked.String())\n\t}\n\treturn nil, errors.New(\"remote image host resolved to no usable IPs\")\n}\n\nfunc isBlockedRichCardImageIP(ip net.IP) bool {\n\taddr, err := netip.ParseAddr(ip.String())\n\tif err != nil {\n\t\treturn true\n\t}\n\taddr = addr.Unmap()\n\treturn !addr.IsGlobalUnicast() ||\n\t\taddr.IsLoopback() ||\n\t\taddr.IsPrivate() ||\n\t\taddr.IsLinkLocalUnicast() ||\n\t\taddr.IsLinkLocalMulticast() ||\n\t\taddr.IsMulticast() ||\n\t\taddr.IsUnspecified() ||\n\t\trichCardImageIPInBlockedPrefix(addr)\n}\n","sourceCodeStart":6657,"sourceCodeEnd":6693,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/feishu.go#L6657-L6693","documentation":"This error comes from the Feishu rich-card image fetcher's custom DNS-aware dialer. After resolving the image host, every returned IP is screened by isBlockedRichCardImageIP (private, loopback, link-local, or otherwise disallowed ranges). If all resolved IPs are blocked and none was previously flagged as blocked-and-skipped, the dialer returns this error, refusing to connect as SSRF protection.","triggerScenarios":"Raised when net.DefaultResolver returns only IP addresses that fail isBlockedRichCardImageIP (loopback 127.0.0.0/8, RFC1918 private ranges, link-local, unspecified, etc.) for the image host, or when resolution yields no usable addresses at all (empty result without a specific blocked IP to report).","commonSituations":"Pointing card image URLs at internal hostnames (localhost, intranet DNS names) that resolve only to private IPs; DNS misconfiguration returning bogus addresses; environments where DNS returns only IPv6 link-local or 0.0.0.0; SSRF probe attempts.","solutions":["Use a public image URL whose hostname resolves to public IP addresses.","Check DNS: run `dig <host>` / `nslookup <host>` and confirm it returns publicly routable IPs, then fix records or use a CDN.","If testing locally, expose the image via a public tunnel/host instead of localhost or an internal name.","Verify your resolver (resolv.conf, corporate DNS, VPN split-DNS) is not mapping public hosts to internal addresses."],"exampleFix":"// before\nimageURL := \"http://intranet.local:8080/avatar.png\" // resolves to 192.168.1.10\n// after\nimageURL := \"https://static.example.com/avatar.png\" // resolves to public IPs","handlingStrategy":"validation","validationCode":"ips, err := net.LookupIP(host)\nif err != nil || len(ips) == 0 {\n    return fmt.Errorf(\"host %s does not resolve\", host)\n}\nfor _, ip := range ips {\n    if isPrivateOrReserved(ip) {\n        return fmt.Errorf(\"host %s resolves to non-public IP %s; image fetch will be blocked\", host, ip)\n    }\n}","typeGuard":null,"tryCatchPattern":"img, err := fetchRichCardImage(ctx, imageURL)\nvar dnsErr *net.DNSError\nif errors.As(err, &dnsErr) || strings.Contains(err.Error(), \"no usable IPs\") {\n    log.Warn(\"image host unusable; using placeholder\", \"url\", imageURL)\n    img = placeholderImage\n}","preventionTips":["Resolve the hostname with dig/nslookup before configuring the URL and confirm public IPs","Avoid hostnames that resolve only to RFC1918/loopback/link-local ranges","Use a public CDN for images rather than intranet servers","Watch for VPN/split-DNS setups that remap public hosts to internal addresses"],"tags":["network","dns","ssrf","feishu","image-download"],"backgroundTag":"dns-resolution-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}