Tencent/tinker · error · IOException
Rest bytes insufficient, expect to read {} bytes but only {}
Error message
Rest bytes insufficient, expect to read {} bytes but only {} bytes were read. What it means
ShareElfFile.readUntilLimit reads exactly bufferOut.limit() bytes from a FileChannel; if the channel returns fewer bytes (including -1 for EOF) it throws IOException with the supplied message plus 'Rest bytes insufficient, expect to read X bytes but only Y bytes were read.'. Within Tinker this is used while parsing ELF files (.so libraries) and OAT/odex headers, so the throw means the file on disk is shorter than the ELF/OAT structure claims — a truncated file.
Source
Thrown at tinker-android/tinker-android-loader-no-op/src/main/java/com/tencent/tinker/loader/shareutil/ShareElfFile.java:116
} else {
return FILE_TYPE_OTHERS;
}
} finally {
if (is != null) {
try {
is.close();
} catch (Throwable thr) {
// Ignored.
}
}
}
}
public static void readUntilLimit(FileChannel channel, ByteBuffer bufferOut, String errMsg) throws IOException {
bufferOut.rewind();
int bytesRead = channel.read(bufferOut);
if (bytesRead != bufferOut.limit()) {
throw new IOException(errMsg + " Rest bytes insufficient, expect to read "
+ bufferOut.limit() + " bytes but only "
+ bytesRead + " bytes were read.");
}
bufferOut.flip();
}
public static String readCString(ByteBuffer buffer) {
final byte[] rawBuffer = buffer.array();
int begin = buffer.position();
while (buffer.hasRemaining() && rawBuffer[buffer.position()] != 0) {
buffer.position(buffer.position() + 1);
}
// Move to the start of next cstring.
buffer.position(buffer.position() + 1);
return new String(rawBuffer, begin, buffer.position() - begin - 1, Charset.forName("ASCII"));
}
public FileChannel getChannel() {View on GitHub (pinned to 1b7ea02c23)
Solutions
- cleanPatch() and re-apply the patch so all native libs restore intact.
- Validate patch file md5 before applying so corrupt downloads never install.
- Ensure sufficient free storage before patch apply.
- If parsing your own ELF files with ShareElfFile, verify file length against the header's e_shoff+e_shnum*e_shentsize before reading.
Example fix
// before
ShareElfFile elf = new ShareElfFile(soFile); // may throw mid-parse
// after: pre-check size plausibility
if (soFile.length() < ElfHeader.MIN_ELF_SIZE) {
throw new IOException("truncated so file: " + soFile);
} Defensive patterns
Strategy: validation
Validate before calling
if (file.length() < 52 /* minimum 64-bit ehdr */) {
throw new IOException("file too small to be ELF: " + file);
} Try / catch
try {
elfFile = new ShareElfFile(soFile);
} catch (IOException e) {
// truncated or corrupt binary: reject and re-download/cleanPatch
} Prevention
- Check free disk space before patch apply
- Verify md5 of downloaded patch files
- Treat short reads during ELF parsing as corruption, not retryable I/O
When it happens
Trigger: Parsing a truncated .so from the patch lib directory or a truncated odex/oat file: the ELF header or section/program header extends past the actual file size. Also triggered when a non-ELF path points at a short file after the magic check passed by luck.
Common situations: Partially downloaded/applied patch leaving truncated .so files; disk-full during patch apply; odex file cut off after an interrupted dexopt; storage corruption on aging devices.
Related errors
- {} Rest bytes insufficient, expect to read {} bytes but only
- patch ${type} extract failed (${message}).
- patch %s extract failed (%s).
- bad elf magic: %x %x %x %x.
- Unexpected elf class: {}
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/30af4734de786fe1.
Report an issue: GitHub.