{"record":{"id":"a5aad63ba72d4bbf","repo":"Tencent/tinker","slug":"map-is-unsorted-at-section","errorCode":null,"errorMessage":"Map is unsorted at ${section}","messagePattern":"Map is unsorted at (.+?)","errorType":"exception","errorClass":"DexException","httpStatus":null,"severity":"error","filePath":"third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/TableOfContents.java","lineNumber":251,"sourceCode":"        Arrays.sort(sections);\n\n        // Skip header section, since its offset must be zero.\n        for (int i = 1; i < sections.length; ++i) {\n            if (sections[i].off == Section.UNDEF_OFFSET) {\n                sections[i].off = sections[i - 1].off;\n            }\n        }\n    }\n\n    public void computeSizesFromOffsets() {\n        int end = fileSize;\n        for (int i = sections.length - 1; i >= 0; i--) {\n            Section section = sections[i];\n            if (section.off == Section.UNDEF_OFFSET) {\n                continue;\n            }\n            if (section.off > end) {\n                throw new DexException(\"Map is unsorted at \" + section);\n            }\n            section.byteCount = end - section.off;\n            end = section.off;\n        }\n\n        dataOff = header.byteCount\n                + stringIds.byteCount\n                + typeIds.byteCount\n                + protoIds.byteCount\n                + fieldIds.byteCount\n                + methodIds.byteCount\n                + classDefs.byteCount;\n\n        dataSize = fileSize - dataOff;\n    }\n\n    private Section getSection(short type) {\n        for (Section section : sections) {","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/TableOfContents.java#L233-L269","documentation":"Thrown by TableOfContents.computeSizesFromOffsets when walking sections from the end of the file backwards: a section's offset is greater than the running end boundary (starting from fileSize). This means a section claims to start after a later section ends — i.e. the offset layout is inconsistent with the declared file size.","triggerScenarios":"A map/header where a section offset exceeds the actual file size (truncated file, fileSize field wrong, or offsets from a pre-truncation version of the dex).","commonSituations":"Truncated downloads or partial writes of dex files; patch pipelines that rewrite headers with a stale fileSize; fat binaries sliced incorrectly.","solutions":["Compare the header fileSize field to the actual byte length of the file — a mismatch means truncation; re-fetch or rebuild the dex.","If you author patchers, recompute fileSize, checksum, and signature together after any content change.","Validate the SHA-1 signature field before parsing to catch truncation early with a clearer error."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"int declaredSize = headerBuf.getInt(32); // fileSize\nif (declaredSize != actualFile.length()) throw new IOException(\"truncated dex: header says \" + declaredSize + \", file is \" + actualFile.length());","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compare declared fileSize to actual byte length before parsing — catches truncation in one cheap check.","Transfer dex artifacts with size/hash verification (e.g. sha1 digest alongside the file)."],"tags":["dex","truncation","section","validation","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}