lionsoul2014/ip2region · error · IOException

incomplete read: read bytes should be ${len}

Error message

incomplete read: read bytes should be ${len}

What it means

loadVectorIndex reads VectorIndexRows*VectorIndexCols*VectorIndexSize bytes starting at HeaderInfoLength and requires a single full read. A short read means the xdb file is smaller than a valid structure or IO failed, so an IOException is thrown.

Source

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

    }

    public static Header loadHeaderFromFile(String xdbPath) throws IOException {
        return loadHeaderFromFile(new File(xdbPath));
    }

    public static Header loadHeaderFromBuffer(LongByteArray cBuffer) throws IOException {
        return new Header(cBuffer.slice(0, HeaderInfoLength));
    }

    // --- read xdb vector index

    public static byte[] loadVectorIndex(RandomAccessFile handle) throws IOException {
        handle.seek(HeaderInfoLength);
        int len = VectorIndexRows * VectorIndexCols * VectorIndexSize;
        final byte[] buff = new byte[len];
        int rLen = handle.read(buff);
        if (rLen != len) {
            throw new IOException("incomplete read: read bytes should be " + len);
        }

        return buff;
    }

    public static byte[] loadVectorIndexFromFile(File xdbFile) throws IOException {
        final RandomAccessFile handle = new RandomAccessFile(xdbFile, "r");
        final byte[] vIndex = loadVectorIndex(handle);
        handle.close();
        return vIndex;
    }

    public static byte[] loadVectorIndexFromFile(String xdbPath) throws IOException {
        return loadVectorIndexFromFile(new File(xdbPath));
    }

    public static byte[] loadVectorIndexFromBuffer(LongByteArray cBuffer) throws IOException {
        final int len = VectorIndexRows * VectorIndexCols * VectorIndexSize;

View on GitHub (pinned to c1a1fc7d59)

Solutions

  1. Re-download or re-copy a complete xdb file and check its size matches the original
  2. Call Searcher.verify(raf) before building the searcher to detect bad files early
  3. Use atomic file publication (write temp file, then rename) so readers never see partial files

Example fix

// before
byte[] vIndex = Searcher.loadVectorIndexFromFile(new File("partial.xdb"));
// after
RandomAccessFile raf = new RandomAccessFile("full.xdb", "r");
Searcher.verify(raf); // throws XdbException on bad files
byte[] vIndex = Searcher.loadVectorIndex(raf);
Defensive patterns

Strategy: validation

Validate before calling

File f = new File(xdbFile);
if (f.length() < 256 + 256*256*4) throw new IllegalStateException("xdb too small for vector index");
Searcher.verify(new RandomAccessFile(f, "r"));

Try / catch

try {
    byte[] vIndex = Searcher.loadVectorIndexFromFile(f);
} catch (IOException e) {
    log.error("failed to load vector index, falling back to file mode", e);
    vIndex = null;
}

Prevention

When it happens

Trigger: Calling Searcher.loadVectorIndex(handle)/loadVectorIndexFromFile on a truncated xdb file (less than 256+vector-index bytes) or on a partially copied/downloaded file.

Common situations: Incomplete uploads of the xdb to servers/containers; using an empty placeholder file; corrupted cache of the xdb in CI.

Related errors


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