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 channel.read returns fewer bytes (short read at end of file), an IOException is thrown with the caller-supplied errMsg prefix plus 'Rest bytes insufficient'. It means the file being parsed as ELF/OAT is truncated relative to the structure the parser expects.
Source
Thrown at tinker-android/tinker-android-loader/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
- Check the file size against the ELF structure before parsing (read the header first and validate offsets/sizes against file length).
- Re-delete the corrupted odex/oat artifact so the system regenerates it (or reinstall the app), then retry patch load.
- If this happens during OTA-related odex checks (ShareOatUtil), disable odex-compatible patching or align patching with the OTA state via isSystemOTA checks.
- Report the exact file path from the log and inspect it with readelf on a pulled copy to confirm truncation.
Example fix
// before
int bytesRead = channel.read(bufferOut);
// after (loop until limit or EOF, then fail with the same message)
int bytesRead = 0;
while (bytesRead < bufferOut.limit()) {
int n = channel.read(bufferOut);
if (n < 0) break;
bytesRead += n;
} Defensive patterns
Strategy: validation
Validate before calling
// Check file is long enough before ELF/OAT parsing
long need = headerSizeEstimate; // e.g. 64 for ELF64 ehdr
if (!file.exists() || file.length() < need) {
throw new IOException("file too short: " + file);
} Try / catch
try {
elfFile = new ShareElfFile(file);
} catch (IOException e) {
// truncated or unreadable ELF: delete artifact so it regenerates, skip oat logic
} Prevention
- Verify file length and md5 before parsing ELF/OAT structures.
- Do not parse odex files while dexopt/installd may still be writing them.
- Treat any short-read failure as corruption and regenerate the artifact.
When it happens
Trigger: Parsing an ELF/OAT file whose declared header sizes (e.g. e_phoff/e_shoff-derived offsets) or section sizes extend past the physical end of file — readUntilLimit is used for the ELF header tail, program headers, section headers, and OAT magic/isa reads.
Common situations: Corrupted or partially-downloaded odex/oat/so files on device; an ELF produced by a toolchain the parser does not model exactly; reading a file while another process (dexopt, instald) is still writing it; sparse file that failed to sync after OTA.
Related errors
- Rest bytes insufficient, expect to read {} bytes but only {}
- Unable to find .rodata section.
- bad elf magic: %x %x %x %x.
- Unable to find .rodata section.
- patch ${type} extract failed (${message}).
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/63f07e97796409e4.
Report an issue: GitHub.