lionsoul2014/ip2region · error · IOException

incomplete read: read bytes should be ${buffer.length}

Error message

incomplete read: read bytes should be ${buffer.length}

What it means

The file-based read() helper seeks to an offset and reads exactly buffer.length bytes; RandomAccessFile.read may return fewer bytes than requested, and this code treats any short read as a corrupted/truncated xdb and throws IOException. It guards against silently using incomplete data.

Source

Thrown at binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java:196

        read(dataPtr, regionBuff);
        return new String(regionBuff, "utf-8");
    }

    protected void read(long offset, byte[] buffer) throws IOException {
        // check the in-memory buffer first
        if (contentBuff != null) {
            contentBuff.copy(offset, buffer, 0, buffer.length);
            return;
        }

        // read from the file handle
        assert handle != null;
        handle.seek(offset);

        this.ioCount++;
        int rLen = handle.read(buffer);
        if (rLen != buffer.length) {
            throw new IOException("incomplete read: read bytes should be " + buffer.length);
        }
    }

    @Override public String toString() {
        return String.format(
            "%s->{version:%s, xdb:%s, vIndex:%s, cBuffer:%s}", 
            super.toString(),
            version.name, xdbFile == null ? "null" : xdbFile.getAbsolutePath(), 
            vectorIndex == null ? "null" : String.valueOf(vectorIndex.length),
            contentBuff == null ? "null" : String.valueOf(contentBuff.length())
        );
    }

    // ---
    // --- static util function
    // --- read xdb header

    public static Header loadHeader(RandomAccessFile handle) throws IOException {

View on GitHub (pinned to c1a1fc7d59)

Solutions

  1. Verify the xdb file integrity (re-download/re-copy it; Searcher.verify can check the header)
  2. Ensure the xdb file is fully written before searchers open it (atomic rename after copy)
  3. Stop sharing the file between writers and readers; open a new searcher after each xdb update
  4. Retry on transient IO errors if the storage layer is unreliable

Example fix

// before
raf = new RandomAccessFile("/data/ip2region.xdb", "r"); // truncated copy
// after
if (new File("/data/ip2region.xdb").length() < expectedSize) throw ...;
raf = new RandomAccessFile("/data/ip2region.xdb.tmp.ready", "r");
Defensive patterns

Strategy: try-catch

Validate before calling

File f = new File(xdbPath);
if (!f.isFile() || f.length() < 256) throw new IllegalStateException("xdb missing/truncated: " + xdbPath);
try (RandomAccessFile raf = new RandomAccessFile(f, "r")) { Searcher.verify(raf); }

Try / catch

try {
    return searcher.search(ip);
} catch (IOException e) {
    log.error("xdb read failed, reloading searcher", e);
    reloadSearcher(); // reopen after integrity check
    return null;
}

Prevention

When it happens

Trigger: Reading the xdb via RandomAccessFile when the file is truncated, being rewritten concurrently, sits on a failing/mounted-later network share, or the offset points past meaningful data.

Common situations: Deploying a partial xdb file; replacing the xdb while searchers still hold open handles; NFS/S3-mounted files with flaky IO.

Related errors


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