chenhg5/cc-connect · warning

remote image host resolved to no usable IPs

Error message

remote image host resolved to no usable IPs

What it means

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.

Source

Thrown at platform/feishu/feishu.go:6675

			ips = append(ips, addr.IP)
		}
	}

	var firstBlocked net.IP
	for _, ip := range ips {
		if isBlockedRichCardImageIP(ip) {
			if firstBlocked == nil {
				firstBlocked = ip
			}
			continue
		}
		dialer := &net.Dialer{Timeout: richCardImageFinalWait}
		return dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port))
	}
	if firstBlocked != nil {
		return nil, fmt.Errorf("remote image host resolved to blocked IP %s", firstBlocked.String())
	}
	return nil, errors.New("remote image host resolved to no usable IPs")
}

func isBlockedRichCardImageIP(ip net.IP) bool {
	addr, err := netip.ParseAddr(ip.String())
	if err != nil {
		return true
	}
	addr = addr.Unmap()
	return !addr.IsGlobalUnicast() ||
		addr.IsLoopback() ||
		addr.IsPrivate() ||
		addr.IsLinkLocalUnicast() ||
		addr.IsLinkLocalMulticast() ||
		addr.IsMulticast() ||
		addr.IsUnspecified() ||
		richCardImageIPInBlockedPrefix(addr)
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Use a public image URL whose hostname resolves to public IP addresses.
  2. Check DNS: run `dig <host>` / `nslookup <host>` and confirm it returns publicly routable IPs, then fix records or use a CDN.
  3. If testing locally, expose the image via a public tunnel/host instead of localhost or an internal name.
  4. Verify your resolver (resolv.conf, corporate DNS, VPN split-DNS) is not mapping public hosts to internal addresses.

Example fix

// before
imageURL := "http://intranet.local:8080/avatar.png" // resolves to 192.168.1.10
// after
imageURL := "https://static.example.com/avatar.png" // resolves to public IPs
Defensive patterns

Strategy: validation

Validate before calling

ips, err := net.LookupIP(host)
if err != nil || len(ips) == 0 {
    return fmt.Errorf("host %s does not resolve", host)
}
for _, ip := range ips {
    if isPrivateOrReserved(ip) {
        return fmt.Errorf("host %s resolves to non-public IP %s; image fetch will be blocked", host, ip)
    }
}

Try / catch

img, err := fetchRichCardImage(ctx, imageURL)
var dnsErr *net.DNSError
if errors.As(err, &dnsErr) || strings.Contains(err.Error(), "no usable IPs") {
    log.Warn("image host unusable; using placeholder", "url", imageURL)
    img = placeholderImage
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/1f34a7adc0b43b6b. Report an issue: GitHub.