lionsoul2014/ip2region · error

invalid ip address(%s expected)

Error message

invalid ip address(%s expected)

What it means

After parsing, Search verifies the byte length of the IP matches the searcher's xdb version (4 bytes for IPv4, 16 for IPv6). A mismatch — e.g. an IPv4 address searched against an IPv6 xdb — returns this error naming the expected version.

Source

Thrown at binding/golang/xdb/searcher.go:128

// Search the region for the specified string or bytes ip address
func (s *Searcher) Search(ip any) (string, error) {
	var err error
	var ipBytes []byte
	switch v := ip.(type) {
	case string:
		ipBytes, err = ParseIP(v)
		if err != nil {
			return "", fmt.Errorf("parse ip %s: %w", v, err)
		}
	case []byte:
		ipBytes = v
	default:
		return "", fmt.Errorf("invalid ip value type %s", v)
	}

	// ip version check
	if len(ipBytes) != s.version.Bytes {
		return "", fmt.Errorf("invalid ip address(%s expected)", s.version.Name)
	}

	// reset the global ioCount
	s.ioCount = 0

	// locate the segment index block based on the vector index
	var il0, il1 = int(ipBytes[0]), int(ipBytes[1])
	var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize
	var sPtr, ePtr = uint32(0), uint32(0)
	if s.vectorIndex != nil {
		sPtr = binary.LittleEndian.Uint32(s.vectorIndex[idx:])
		ePtr = binary.LittleEndian.Uint32(s.vectorIndex[idx+4:])
	} else if s.contentBuff != nil {
		sPtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx:])
		ePtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx+4:])
	} else {
		// read the vector index block
		var buff = make([]byte, VectorIndexSize)

View on GitHub (pinned to c1a1fc7d59)

Solutions

  1. Load the matching xdb file version for your input (xdb_v4 for IPv4 IPs, xdb_v6 for IPv6).
  2. Detect the IP family first (len of net.ParseIP(ip).To4()==4) and route to the appropriate searcher.
  3. Normalize with To4()/To16() so the byte length always matches the searcher's version.
  4. Use IPVersion from the header (via LoadHeader) to pick the right searcher automatically.

Example fix

// before
region, err := v6Searcher.Search(ctx, "1.2.3.4") // wrong family
// after
ip := net.ParseIP("1.2.3.4")
if ip.To4() != nil {
    region, err = v4Searcher.Search(ctx, ip.String())
} else {
    region, err = v6Searcher.Search(ctx, ip.String())
}
Defensive patterns

Strategy: validation

Validate before calling

ip := net.ParseIP(raw)
ipBytes := ip.To4()
if ipBytes == nil { ipBytes = ip.To16() }
want := 16
if header.IPVersion == xdb.IPv4 { want = 4 }
if len(ipBytes) != want {
    return fmt.Errorf("ip family mismatch: got %d bytes, xdb expects %d", len(ipBytes), want)
}

Type guard

func matchesXDBVersion(raw string, ver xdb.IPVersion) bool {
    ip := net.ParseIP(raw)
    if ip == nil { return false }
    if ver == xdb.IPv4 { return ip.To4() != nil }
    return ip.To4() == nil
}

Try / catch

region, err := searcher.Search(ctx, raw)
if err != nil && strings.Contains(err.Error(), "invalid ip address(") {
    // route to the other-version searcher or drop the record
    return handleVersionMismatch(raw, err)
}

Prevention

When it happens

Trigger: Searching "1.2.3.4" against an xdb_v6.xdb (or an IPv6 literal against an xdb_v4.xdb); passing a 4-byte slice where 16 bytes are required, or vice versa; passing an unexpanded IPv4-mapped form with wrong length.

Common situations: Mixing v4 and v6 xdb files in one app and reusing one searcher for all inputs; log data mixing IPv4 and IPv6 addresses; using To4() output on an IPv6 searcher.

Related errors


AI-assisted analysis of lionsoul2014/ip2region@c1a1fc7d59 (2026-09-02). Data as JSON: /api/errors/d44425e9f10a6baf. Report an issue: GitHub.