Tencent/tinker · error · DexException
Unexpected magic: ${magic}
Error message
Unexpected magic: ${magic} What it means
Thrown by TableOfContents.readHeader when the first 8 bytes are not a recognized dex magic ("dex\n035\0" style, validated via DexFormat.magicToApi returning -1). The magic both identifies the format and encodes the version; anything unparseable is rejected before any other header field is read.
Source
Thrown at third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/TableOfContents.java:170
}
}
public void readFrom(Dex dex) throws IOException {
readHeader(dex.openSection(header));
// special case, since mapList.byteCount is available only after
// computeSizesFromOffsets() was invoked, so here we can't use
// dex.openSection(mapList) to get dex section. Or
// an {@code java.nio.BufferUnderflowException} will be thrown.
readMap(dex.openSection(mapList.off));
computeSizesFromOffsets();
}
private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException {
byte[] magic = headerIn.readByteArray(8);
api = DexFormat.magicToApi(magic);
if (api == -1) {
throw new DexException("Unexpected magic: " + Arrays.toString(magic));
}
checksum = headerIn.readInt();
signature = headerIn.readByteArray(20);
fileSize = headerIn.readInt();
int headerSize = headerIn.readInt();
if (headerSize != SizeOf.HEADER_ITEM) {
throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize));
}
int endianTag = headerIn.readInt();
if (endianTag != DexFormat.ENDIAN_TAG) {
throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
}
linkSize = headerIn.readInt();
linkOff = headerIn.readInt();
mapList.off = headerIn.readInt();
if (mapList.off == 0) {
throw new DexException("Cannot merge dex files that do not contain a map");View on GitHub (pinned to 1b7ea02c23)
Solutions
- Confirm the input really is a standard dex: hexdump the first 8 bytes and compare to "dex\n035\0" (or 036/037/038).
- If the artifact is a compact dex, convert it back: use the toolchain's conversion (e.g. `dexdump`/profgen or d8 with the extracted dex) or extract the original dex from the apk instead of on-device artifacts.
- Skip non-dex files in batch pipelines by checking the magic before handing the buffer to Dex.
Example fix
// before
Dex dex = new Dex(new File(dir, name)); // blows up on odex/cdex
// after
byte[] head = new byte[8];
try (RandomAccessFile raf = new RandomAccessFile(f, "r")) {
raf.readFully(head);
}
if (!new String(head, StandardCharsets.US_ASCII).startsWith("dex\n")) continue;
Dex dex = new Dex(f); Defensive patterns
Strategy: validation
Validate before calling
static boolean isStandardDex(byte[] head8) {
return head8 != null && head8.length >= 4 && head8[0]=='d' && head8[1]=='e' && head8[2]=='x' && head8[3]=='\n';
}
// use before new Dex(...): read 8 bytes, reject cdex/odex/non-dex Try / catch
catch (DexException e) with 'Unexpected magic' -> skip file in batch runs, or convert compact-dex source before retry
Prevention
- Filter build artifacts by magic bytes, not file extension, before dex processing.
- Extract dex from apks on the build machine rather than pulling odex/vdex from devices.
When it happens
Trigger: new Dex(bytes) or dex merging where the input is not a raw dex: a compact-dex (cdex, magic "cdex001"—ART on-device layout), a zip passed as bytes, an ELF/.odex, or a truncated file missing the header.
Common situations: Pointing the tool at build outputs of the wrong artifact type (apks containing compact dex from Android 8.1+ build tooling), extracted odex/vdex files, or empty/HTML error pages saved with a .dex name.
Related errors
- Unexpected header: 0x${headerSize}
- Unexpected endian tag: 0x${endianTag}
- Cannot merge dex files that do not contain a map
- file is null.
- Expected ${DEX_IN_JAR_NAME} in ${file}
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/914e593bed5058d6.
Report an issue: GitHub.