lionsoul2014/ip2region · error

read segment index at %d: %w

Error message

read segment index at %d: %w

What it means

Go xdb searcher: reading one fixed-size segment-index row (during the binary search over the index block) failed via the internal read(); the wrapped error is a seek/read/short-read failure against the file or content buffer, indicating a truncated or corrupt xdb.

Source

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

	// fmt.Printf("sPtr=%d, ePtr=%d\n", sPtr, ePtr)
	// @Note: ptr validate, zero ptr means source data missing
	// so we could just stop here and return an empty string.
	if sPtr == 0 || ePtr == 0 {
		return "", nil
	}

	// binary search the segment index to get the region
	var bytes, dBytes = len(ipBytes), len(ipBytes) << 1
	var segIndexSize = uint32(s.version.SegmentIndexSize)
	var dataLen, dataPtr = 0, uint32(0)
	var buff = make([]byte, segIndexSize)
	var l, h = 0, int((ePtr - sPtr) / segIndexSize)
	for l <= h {
		m := (l + h) >> 1
		p := sPtr + uint32(m)*segIndexSize
		err := s.read(int64(p), buff)
		if err != nil {
			return "", fmt.Errorf("read segment index at %d: %w", p, err)
		}

		// decode the data step by step to reduce the unnecessary operations
		if s.version.IPCompare(ipBytes, buff[0:bytes]) < 0 {
			h = m - 1
		} else if s.version.IPCompare(ipBytes, buff[bytes:dBytes]) > 0 {
			l = m + 1
		} else {
			dataLen = int(binary.LittleEndian.Uint16(buff[dBytes:]))
			dataPtr = binary.LittleEndian.Uint32(buff[dBytes+2:])
			break
		}
	}

	// fmt.Printf("dataLen: %d, dataPtr: %d\n", dataLen, dataPtr)
	if dataLen == 0 {
		return "", nil
	}

View on GitHub (pinned to c1a1fc7d59)

Solutions

  1. Verify file integrity (size vs header end-pointer; compare checksum if available) and re-download the xdb.
  2. Ensure content-buffer searchers get the entire file, matching header.StartIndexPtr/HeaderInfoLength+IndexSize expectations.
  3. Check disk space / permissions for file-based searchers.
  4. Inspect the wrapped OS error to distinguish seek failure from short read.

Example fix

// before
st, _ := os.Stat(path)
// no size check, searcher reads garbage index
// after
st, err := os.Stat(path)
if err != nil { return err }
hdr, err := xdb.LoadHeaderFromFile(path)
if err != nil { return err }
if int64(hdr.Length) > st.Size() {
    return fmt.Errorf("xdb truncated: have %d, need %d", st.Size(), hdr.Length)
}
Defensive patterns

Strategy: validation

Validate before calling

fi, _ := os.Stat(xdbPath)
hdr, err := xdb.LoadHeaderFromFile(xdbPath)
if err != nil { return err }
if fi.Size() < int64(hdr.Length) {
    return fmt.Errorf("xdb truncated: %d < %d", fi.Size(), hdr.Length)
}

Try / catch

region, err := searcher.Search(ctx, ip)
if err != nil {
    if strings.Contains(err.Error(), "read segment index") {
        return nil, fmt.Errorf("xdb index corrupt — regenerate the xdb file")
    }
    return err
}

Prevention

When it happens

Trigger: File-based read failure (bad seek/short read) at segment index offset p; a corrupted or truncated xdb whose index area is shorter than the header's start/end pointers claim; content buffer missing the index tail.

Common situations: Truncated download cutting off the index section; corrupted xdb produced by a failed maker run; searching an xdb loaded from a partially cached buffer.

Related errors


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