lionsoul2014/ip2region · error
handle read: %w
Error message
handle read: %w
What it means
Go xdb searcher file-mode read: the Read after a successful Seek failed (or the handle was invalidated); the wrapped error carries the OS-level read failure — usually the xdb file was removed, replaced, or the disk errored mid-search.
Source
Thrown at binding/golang/xdb/searcher.go:222
// do the data read operation based on the setting.
// content buffer first or will read from the file.
// this operation will invoke the Seek for file based read.
func (s *Searcher) read(offset int64, buff []byte) error {
if s.contentBuff != nil {
cLen := copy(buff, s.contentBuff[offset:])
if cLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
} else {
_, err := s.dbReader.Seek(offset, 0)
if err != nil {
return fmt.Errorf("seek to %d: %w", offset, err)
}
s.ioCount++
rLen, err := s.dbReader.Read(buff)
if err != nil {
return fmt.Errorf("handle read: %w", err)
}
if rLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
}
return nil
}
View on GitHub (pinned to c1a1fc7d59)
Solutions
- Verify the xdb file size matches header.Length and re-download if truncated.
- Reopen the searcher after the file changes.
- Retry once on transient I/O errors; xdb reads are stateless (each read seeks first).
- Inspect the wrapped cause for io.EOF vs fs-level errors.
Example fix
// before
region, err := searcher.Search(ctx, ip)
// after
region, err := searcher.Search(ctx, ip)
if err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, syscall.EIO) {
searcher, err = reloadSearcher() // reopen + retry
}
} Defensive patterns
Strategy: retry
Validate before calling
fi, _ := os.Stat(xdbPath)
hdr, _ := xdb.LoadHeaderFromFile(xdbPath)
if fi.Size() < int64(hdr.Length) {
return fmt.Errorf("xdb truncated on disk")
} Try / catch
var region string
var err error
for i := 0; i < 2; i++ {
region, err = searcher.Search(ctx, ip)
if err == nil { break }
if !strings.Contains(err.Error(), "handle read") { break }
searcher, _ = reopenSearcher() // transient I/O: reopen once and retry
} Prevention
- Verify xdb completeness at deploy time (size vs header.Length).
- Keep xdb on local disk, not network mounts, for hot paths.
- Replace the file atomically and reload the searcher on update.
- Retry once on transient read errors — xdb reads are idempotent seeks.
When it happens
Trigger: Disk/network I/O error mid-read; reading at an offset at/after EOF because the xdb is truncated or the offset is corrupted; reading from a special/pipe-backed file that errors on read.
Common situations: Truncated xdb on disk; flaky NFS/S3-mounted file; file replaced by a shorter version while a searcher holds the old descriptor.
Related errors
- read vector index block at %d: %w
- read segment index at %d: %w
- read region at %d: %w
- seek to %d: %w
- failed to fread from {$len}
AI-assisted analysis of lionsoul2014/ip2region@c1a1fc7d59 (2026-09-02).
Data as JSON: /api/errors/df56b21576c856f7.
Report an issue: GitHub.