lionsoul2014/ip2region · error

read vector index block at %d: %w

Error message

read vector index block at %d: %w

What it means

While locating the IP, Search reads the 8-byte vector index block for the target's 5-bit prefix. If the underlying read (file seek+read or content-buffer copy) fails, the error is wrapped with the absolute block offset for diagnosis.

Source

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

	// 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)
		err := s.read(int64(HeaderInfoLength+idx), buff)
		if err != nil {
			return "", fmt.Errorf("read vector index block at %d: %w", HeaderInfoLength+idx, err)
		}

		sPtr = binary.LittleEndian.Uint32(buff)
		ePtr = binary.LittleEndian.Uint32(buff[4:])
	}

	// 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)

View on GitHub (pinned to c1a1fc7d59)

Solutions

  1. Check the xdb file size and integrity; the vector area near the file start must be intact.
  2. If using a content buffer, ensure it is the COMPLETE xdb file bytes, not a prefix.
  3. Reload the searcher after the file changes; don't keep stale file handles.
  4. Inspect the wrapped cause (%w) for the OS-level error (permission denied, EOF, etc.).

Example fix

// before
cFile, _ := os.ReadFile(path)
searcher, _ := xdb.NewSearcherWithContent(header, cFile[:1000])
// after
cFile, err := os.ReadFile(path)
if err != nil { return err }
searcher, err := xdb.NewSearcherWithContent(header, cFile) // full content
Defensive patterns

Strategy: try-catch

Validate before calling

st, err := os.Stat(xdbPath)
if err != nil { return err }
if st.Size() < int64(xdb.VectorIndexSize*8+xdb.HeaderInfoLength) {
    return fmt.Errorf("xdb too small for vector index")
}

Type guard

func hasFullVectorArea(content []byte) bool {
    return len(content) >= xdb.HeaderInfoLength+8*xdb.VectorIndexSize
}

Try / catch

region, err := searcher.Search(ctx, ip)
if err != nil {
    if strings.Contains(err.Error(), "read vector index block") {
        searcher, err = reloadSearcher() // full re-read of the xdb
    }
    return err
}

Prevention

When it happens

Trigger: File-based searcher: xdb file unreadable, deleted, or I/O error at offset HeaderInfoLength+idx; content-buffer searcher: contentBuff shorter than the vector region because the wrong buffer was supplied to LoadContentFromBuff/NewSearcherWithContent.

Common situations: xdb file replaced or truncated while a long-lived searcher holds it open; passing a header-only buffer instead of the whole file content to the content-buffer searcher; disk/permission errors.

Related errors


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