Tencent/tinker · error · IOException
Bad oat version: {}
Error message
Bad oat version: {} What it means
ShareOatUtil reads the 3 ASCII version bytes at offset 4 of the oat header and requires them to parse as an integer (e.g. '007', '064'). NumberFormatException triggers IOException("Bad oat version: <str>"). The oat header exists but its version field is not a recognizable OAT version.
Source
Thrown at tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/shareutil/ShareOatUtil.java:85
|| 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);
}
ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.order(elfFile.getDataOrder());
// TODO This is a risk point, since each oat version may use a different offset.
// So far it's ok. Perhaps we should use oatVersionNum to judge the right offset in
// the future.
final int isaNumOffsetFromOatBegin = 12;
channel.position(roDataHdr.shOffset + isaNumOffsetFromOatBegin);
buffer.limit(4);
ShareElfFile.readUntilLimit(channel, buffer, "Failed to read isa num.");
int isaNum = buffer.getInt();
if (isaNum < 0 || isaNum >= InstructionSet.values().length) {
throw new IOException("Bad isa num: " + isaNum);
}
switch (InstructionSet.values()[isaNum]) {View on GitHub (pinned to 1b7ea02c23)
Solutions
- Verify the oat version bytes with a hex dump of .rodata offset 4..7.
- Fall back to Build.CPU_ABI-derived instruction set when parsing fails.
- Update Tinker to a release that supports the device's ART version.
- Catch Throwable from getOatFileInstructionSet and degrade gracefully.
Defensive patterns
Strategy: fallback
Try / catch
try {
isa = ShareOatUtil.getOatFileInstructionSet(odexFile);
} catch (IOException ioe) {
// 'Bad oat version': unsupported oat version, fall back to ABI-derived ISA
isa = ShareTinkerInternals.getCurrentInstructionSet();
} Prevention
- Track oat header changes per Android release when supporting new versions.
- Always provide an instruction-set fallback when probing odex files.
- Report unknown oat versions (log the parsed string) to catch format drift early.
When it happens
Trigger: getOatFileInstructionSet on an odex whose .rodata starts with 'oat\n' but whose following 3 bytes are not digits — format change across ART versions or corruption.
Common situations: ART version layout changes on newer Android releases (the code itself carries a TODO about version-dependent offsets); custom ROMs; partially corrupted odex files.
Related errors
- Unable to find .rodata section.
- Unable to find .rodata section.
- Bad oat magic: %x %x %x %x
- Bad isa num: {}
- Bad isa num: {}
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/fc1e9b5b4d423a3c.
Report an issue: GitHub.