chinabugotech/hutool · error · IOException

File length is [{}] but read [{}]!

Error message

File length is [{}] but read [{}]!

What it means

After allocating a buffer of exactly file.length() bytes and calling FileInputStream.read(bytes), Hutool verifies that the number of bytes actually read equals the file length. If readLength < len it throws an IOException with the formatted length/read mismatch, which the surrounding catch (Exception e) wraps into IORuntimeException. This guards against three root causes: the file was truncated or rewritten between the length() call and the read (TOCTOU), the filesystem reports an inaccurate length (special files such as /proc/*, named pipes, device files), or a single read() returned early without filling the buffer.

Source

Thrown at hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java:126

	 * 文件的长度不能超过 {@link Integer#MAX_VALUE}
	 *
	 * @return 字节码
	 * @throws IORuntimeException IO异常
	 */
	public byte[] readBytes() throws IORuntimeException {
		long len = file.length();
		if (len >= Integer.MAX_VALUE) {
			throw new IORuntimeException("File is larger then max array size");
		}

		byte[] bytes = new byte[(int) len];
		FileInputStream in = null;
		int readLength;
		try {
			in = new FileInputStream(file);
			readLength = in.read(bytes);
			if(readLength < len){
				throw new IOException(StrUtil.format("File length is [{}] but read [{}]!", len, readLength));
			}
		} catch (Exception e) {
			throw new IORuntimeException(e);
		} finally {
			IoUtil.close(in);
		}

		return bytes;
	}

	/**
	 * 读取文件内容
	 *
	 * @return 内容
	 * @throws IORuntimeException IO异常
	 */
	public String readString() throws IORuntimeException{
		return new String(readBytes(), this.charset);

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Ensure the file is not concurrently modified during the read (lock it, or copy it to a stable temp file first).
  2. For special/pseudo files, stream with getInputStream()/readLines() instead of readBytes(), since their reported length is unreliable.
  3. Wait until the file is fully written (download complete / writer closed) before reading.
  4. If transient, retry the read once the file stabilizes; on persistent mismatch, inspect getCause() of the IORuntimeException for the underlying IOException.

Example fix

// before: assumes file is stable and length() is accurate
byte[] data = FileReader.create(file).readBytes();

// after: stream from a possibly-unstable / special file
try (InputStream in = FileReader.create(stableCopy).getInputStream()) {
    // read incrementally; never trusts a single length() value
    byte[] chunk = in.readNBytes(8192);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// For special/proc files, skip readBytes entirely:
if (file.getPath().startsWith("/proc") || file.getPath().startsWith("/sys")
        || !file.isFile()) {
    // stream instead; do NOT trust file.length()
    return;
}
// For regular files that may be concurrently written, copy to a stable temp first:
Path tmp = Files.createTempFile("read-", ".tmp");
try {
    Files.copy(file.toPath(), tmp, StandardCopyOption.REPLACE_EXISTING);
    byte[] data = FileReader.create(tmp.toFile()).readBytes();
} finally {
    Files.deleteIfExists(tmp);
}

Type guard

boolean lengthIsReliable(File f) {
    if (f == null || !f.isFile()) return false;
    String p = f.getPath();
    // pseudo-filesystems report inaccurate lengths
    return !p.startsWith("/proc") && !p.startsWith("/sys") && !p.startsWith("/dev");
}

Try / catch

try {
    byte[] data = FileReader.create(f).readBytes();
} catch (IORuntimeException e) {
    Throwable cause = e.getCause();
    if (cause instanceof IOException && String.valueOf(cause.getMessage())
            .contains("File length is") && cause.getMessage().contains("but read")) {
        // likely truncation/short-read: stream instead, or retry after stabilization
        try (InputStream in = new BufferedInputStream(new FileInputStream(f))) {
            // consume fully with a read-loop
        }
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling FileReader.readBytes() (directly or via readString()) when: the file is concurrently modified/truncated between file.length() and in.read(); the path points at a special/proc or pseudo-file whose size() is wrong; FileInputStream.read(byte[]) returns fewer bytes than requested in a single invocation on some network/overlay filesystems; the file is actively being written/downloaded and shrinks mid-read.

Common situations: Concurrent log rotation truncating a file during read; reading Linux pseudo-files (/proc, /sys) that report 0 or stale lengths; reading a file still being streamed/downloaded; race conditions in multi-process pipelines; FUSE/network filesystems where read() may short-return.

Related errors


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/f8ae1e63428211e8. Report an issue: GitHub.