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

  1. Confirm the input really is a standard dex: hexdump the first 8 bytes and compare to "dex\n035\0" (or 036/037/038).
  2. 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.
  3. 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

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


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/914e593bed5058d6. Report an issue: GitHub.