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
- Check the xdb file size and integrity; the vector area near the file start must be intact.
- If using a content buffer, ensure it is the COMPLETE xdb file bytes, not a prefix.
- Reload the searcher after the file changes; don't keep stale file handles.
- 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
- Use the content-buffer searcher to eliminate per-search file I/O.
- Never mutate or slice the content buffer after creating the searcher.
- Reload the searcher when the xdb file changes on disk.
- Log the offset in the wrapped error to spot corruption patterns.
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
- read segment index at %d: %w
- read region at %d: %w
- seek to vector index: %w
- invalid input buffer
- incomplete read: readed bytes should be %d
AI-assisted analysis of lionsoul2014/ip2region@c1a1fc7d59 (2026-09-02).
Data as JSON: /api/errors/060d63cc1b957ebc.
Report an issue: GitHub.