Tencent/tinker · error · IOException
Bad oat magic: %x %x %x %x
Error message
Bad oat magic: %x %x %x %x
What it means
ShareOatUtil.readOatInstructionSet reads 8 bytes at the start of the .rodata section header offset of an ELF file and validates the OAT magic bytes 'o','a','t','\n'. If those four bytes do not spell the OAT magic, the file at that offset is not a valid OAT/odex container, so an IOException with the hex dump of the offending bytes is thrown. This is a hard integrity check: Tinker refuses to derive an instruction set from a file that is not a real oat file.
Source
Thrown at tinker-android/tinker-android-loader-no-op/src/main/java/com/tencent/tinker/loader/shareutil/ShareOatUtil.java:68
String result = "";
try {
elfFile = new ShareElfFile(oatFile);
final ShareElfFile.SectionHeader roDataHdr = elfFile.getSectionHeaderByName(".rodata");
if (roDataHdr == null) {
throw new IOException("Unable to find .rodata section.");
}
final FileChannel channel = elfFile.getChannel();
channel.position(roDataHdr.shOffset);
final byte[] oatMagicAndVersion = new byte[8];
ShareElfFile.readUntilLimit(channel, ByteBuffer.wrap(oatMagicAndVersion), "Failed to read oat magic and version.");
if (oatMagicAndVersion[0] != 'o'
|| oatMagicAndVersion[1] != 'a'
|| oatMagicAndVersion[2] != 't'
|| oatMagicAndVersion[3] != '\n') {
throw new IOException(
String.format("Bad oat magic: %x %x %x %x",
oatMagicAndVersion[0],
oatMagicAndVersion[1],
oatMagicAndVersion[2],
oatMagicAndVersion[3])
);
}
final int versionOffsetFromOatBegin = 4;
final int versionBytes = 3;
final String oatVersion = new String(oatMagicAndVersion,
versionOffsetFromOatBegin, versionBytes, Charset.forName("ASCII"));
try {
Integer.parseInt(oatVersion);
} catch (NumberFormatException e) {
throw new IOException("Bad oat version: " + oatVersion);
}View on GitHub (pinned to 1b7ea02c23)
Solutions
- Verify the odex file is a real OAT file before parsing: check SharePatchFileUtil.isLegalFile(file) and that the ELF header/section parse succeeded.
- Delete the stale odex and re-trigger dex2oat (retry patch install or call TinkerDexOptimizer again) so a fresh odex is generated on-device.
- If the device consistently produces invalid oat files (known vendor bypass), skip the oat check path that this code guards (the code itself notes it is 'fine to skip the check' for such devices).
- Confirm the patch package was built with a compatible tinker version and the correct ABI so dex2oat emits a matching oat.
Example fix
// before
String isa = ShareOatUtil.readOatInstructionSet(oatFile); // throws on stub odex
// after
if (SharePatchFileUtil.isLegalFile(oatFile)) {
String isa = null;
try {
isa = ShareOatUtil.readOatInstructionSet(oatFile);
} catch (IOException e) {
ShareTinkerLog.w(TAG, "bad oat, delete and re-optimize: " + oatFile, e);
oatFile.delete();
}
} Defensive patterns
Strategy: try-catch
Validate before calling
File oat = new File(oatDir, name + ".odex");
if (!oat.exists() || oat.length() < 8) {
// no valid oat to parse; trigger re-optimization instead
} Try / catch
try { isa = ShareOatUtil.readOatInstructionSet(oatFile); } catch (IOException e) { oatFile.delete(); /* regenerate odex */ } Prevention
- Always gate oat parsing behind SharePatchFileUtil.isLegalFile(file).
- Treat odex files as disposable caches: delete and re-run dex2oat on any parse failure instead of retrying the same bytes.
- Test patch flow on vendor ROMs known to bypass dex2oat (vivo, oppo).
When it happens
Trigger: Calling the oat-parsing path (e.g. ShareTinkerInternals/SharePatchFileUtil flows that read the odex generated for a patch dex) with: a truncated or corrupt odex; an ELF that is a plain dex/zip instead of an oat; a file whose section header table (roDataHdr.shOffset) pointed past the real data due to a mismatched ELF parse; a zero-length or placeholder odex written by a device that skipped dex2oat.
Common situations: Vendor ROMs (vivo/oppo-class, per the code comments) that bypass dex2oat and emit nothing or a stub file; patch packages rebuilt with a different dex2oat than the one on device; OTA updates changing the ART/oat format; manually copying an odex from another build.
Related errors
- Bad oat version: {}
- bad elf magic: %x %x %x %x.
- Bad oat magic: %x %x %x %x
- patch dex opt file not found, but path is null!!!!
- bad elf magic: %x %x %x %x.
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/9b1d2a8af3e4e33e.
Report an issue: GitHub.