pxb1988/dex2jar · error · DexException

fail to load string @%08x

Error message

fail to load string %d@%08x

What it means

When resolving a string id, Mutf8.decode hit a UTFDataFormatException while decoding the MUTF-8 bytes of the string_data_item at the given offset. The reader wraps it in a DexException with the string id and hex offset so you can locate the bad entry. The dex's string table contains bytes that are not valid MUTF-8 or run past the end of the data section.

Solutions

  1. Re-obtain or re-extract the dex file — verify checksum/signature (the dex header stores both)
  2. Use the id@offset in the message to hexdump the failing string_data_item and check the bytes
  3. If you control the source, rebuild with d8/dx to regenerate a valid string table
  4. For robustness, patch Mutf8.decode usage to substitute invalid sequences instead of throwing if you must tolerate dirty inputs

Example fix

// before
// java -jar dex2jar.jar broken.apk -> DexException: fail to load string 42@0001a2b0
// after
// adb shell or unzip -t app.apk  # verify archive integrity, re-extract classes.dex
// then check header: head -c 12 classes.dex | xxd (magic + checksum)
Defensive patterns

Strategy: validation

Validate before calling

static boolean dexChecksumOk(byte[] dex) throws java.security.NoSuchAlgorithmException {
    if (dex.length < 12) return false;
    java.util.zip.Adler32 a = new java.util.zip.Adler32();
    a.update(dex, 12, dex.length - 12);
    int stored = (dex[8]&0xFF) | (dex[9]&0xFF)<<8 | (dex[10]&0xFF)<<16 | (dex[11]&0xFF)<<24;
    return (int) a.getValue() == stored;
}
// reject files failing the Adler-32 checksum before DexFileReader touches the string table

Try / catch

try {
    new DexFileReader(file).accept(visitor);
} catch (DexException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("fail to load string")) {
        throw new java.io.IOException("Corrupt DEX string table in " + file
            + " (" + e.getMessage() + "); re-extract or rebuild the dex", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A string_ids/string_data entry whose modified-UTF-8 is invalid (e.g. raw surrogates encoded wrongly, truncated continuation bytes) or whose offset points outside valid data — corrupted dex, bad string_ids offsets, or non-dex bytes in the string section.

Common situations: Corrupted downloads/extractions of APKs; dex edited by string-rewriting tools that broke MUTF-8 encoding; malicious/packed apps with invalid string tables processed by dex2jar.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of pxb1988/dex2jar@b5bda4fb49 (2026-09-08). Data as JSON: /api/errors/5e27884334ed1346. Report an issue: GitHub.

Appendix: source

Thrown at dex-reader/src/main/java/com/googlecode/d2j/reader/DexFileReader.java:973

        methoIdIn.position(id * 8);
        int owner_idx = 0xFFFF & methoIdIn.getShort();
        int proto_idx = 0xFFFF & methoIdIn.getShort();
        int name_idx = methoIdIn.getInt();
        return new Method(getType(owner_idx), getString(name_idx), getProto(proto_idx));
    }

    private String getString(int id) {
        if (id == -1) {
            return null;
        }
        int offset = stringIdIn.getInt(id * 4);
        stringDataIn.position(offset);
        int length = readULeb128i(stringDataIn);
        try {
            StringBuilder buff = new StringBuilder((int) (length * 1.5));
            return Mutf8.decode(stringDataIn, buff);
        } catch (UTFDataFormatException e) {
            throw new DexException(e, "fail to load string %d@%08x", id, offset);
        }
    }

    private String getType(int id) {
        if (id == -1) {
            return null;
        }
        return getString(typeIdIn.getInt(id * 4));
    }

    private int acceptField(ByteBuffer in, int lastIndex, DexClassVisitor dcv,
            Map<Integer, Integer> fieldAnnotationPositions, Object value, int config) {
        int diff = readULeb128i(in);
        int field_access_flags = readULeb128i(in);
        int field_id = lastIndex + diff;
        Field field = getField(field_id);
        // //////////////////////////////////////////////////////////////
        DexFieldVisitor dfv = dcv.visitField(field_access_flags, field, value);

View on GitHub (pinned to b5bda4fb49)