lionsoul2014/ip2region · critical · Error

incomplete read (${rBytes} read, ${stats.size} expected)

Error message

incomplete read (${rBytes} read, ${stats.size} expected)

What it means

loadContent stats the fd and reads the entire file (stats.size bytes) from offset 0 into one Buffer for the ContentCache policy. If the read returns fewer bytes than fstat reported, the library throws rather than caching an incomplete copy of the data region.

Source

Thrown at binding/javascript/util.js:376

    if (rBytes != vBytes) {
        throw new Error(`incomplete read (${rBytes} read, ${vBytes} expected)`);
    }
    return buffer;
}

export function loadVectorIndexFromFile(dbPath) {
    const fd = fs.openSync(dbPath, "r");
    const vIndex = loadVectorIndex(fd);
    fs.closeSync(fd);
    return vIndex;
}

export function loadContent(fd) {
    const stats = fs.fstatSync(fd);
    const buffer = Buffer.alloc(stats.size);
    const rBytes = fs.readSync(fd, buffer, 0, buffer.length, 0);
    if (rBytes != stats.size) {
        throw new Error(`incomplete read (${rBytes} read, ${stats.size} expected)`);
    }
    return buffer;
}

export function loadContentFromFile(dbPath) {
    const fd = fs.openSync(dbPath, "r");
    const content = loadContent(fd);
    fs.closeSync(fd);
    return content;
}

// --- 

// Verify if the current Searcher could be used to search the specified xdb file.
// Why do we need this check ?
// The future features of the xdb impl may cause the current searcher not able to work properly.
//
// @Note: You Just need to check this ONCE when the service starts

View on GitHub (pinned to c1a1fc7d59)

Solutions

  1. Re-copy the xdb file, verify integrity (size/checksum), and restart or reload the searcher.
  2. Stop concurrent writers before loading, or atomically swap via rename and reopen the fd after.
  3. Retry the load — a transient short read often succeeds on the second attempt.
  4. Add a pre-check comparing fstatSync(fd).size against the minimum expected xdb size.

Example fix

// before
const buf = loadContent(fd); // short read -> throws
// after
let buf;
for (let i = 0; i < 3 && !buf; i++) {
  try { buf = loadContent(fd); } catch (e) { if (!/incomplete read/.test(e.message)) throw e; }
}
if (!buf) throw new Error('xdb content repeatedly short-read; file may be truncated');
Defensive patterns

Strategy: retry

Validate before calling

const size = fs.fstatSync(fd).size;
if (size === 0) throw new Error('xdb file is empty');
// optionally: minimum plausible size check before full-content cache load

Try / catch

try {
  contentBuf = loadContent(fd);
} catch (e) {
  if (String(e.message).includes('incomplete read')) {
    // reopen the file (it may have been swapped) and retry once
    fd = fs.openSync(dbPath, 'r');
    contentBuf = loadContent(fd);
  } else throw e;
}

Prevention

When it happens

Trigger: loadContent(fd) / content() where the file shrinks between fstatSync and readSync (concurrent writer/replacer), a truncated file, or I/O returning short reads on an unreliable/network filesystem.

Common situations: Deploy systems that swap the .xdb via rename while searchers hold open fds; NFS/EFS hiccups; file being regenerated by the xdb maker while the app starts.

Related errors


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