Tencent/tinker · error · DexException
unknown output extension: ${file}
Error message
unknown output extension: ${file} What it means
Thrown by Dex(File) when the file name does not end with a recognized archive suffix (.zip/.jar/.apk via FileUtils.hasArchiveSuffix) nor with .dex. The constructor dispatches purely on the file-name extension, so any other spelling is rejected without reading the file.
Source
Thrown at third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/Dex.java:176
}
} else if (file.getName().endsWith(".dex")) {
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
loadFrom(in, (int) file.length());
} catch (Exception e) {
throw new DexException(e);
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {
// ignored.
}
}
}
} else {
throw new DexException("unknown output extension: " + file);
}
}
private static void checkBounds(int index, int length) {
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
}
}
private void loadFrom(InputStream in) throws IOException {
loadFrom(in, 0);
}
private void loadFrom(InputStream in, int initSize) throws IOException {
byte[] rawData = FileUtils.readStream(in, initSize);
this.data = ByteBuffer.wrap(rawData);
this.data.order(ByteOrder.LITTLE_ENDIAN);
this.tableOfContents.readFrom(this);View on GitHub (pinned to 1b7ea02c23)
Solutions
- Rename/copy the file to a literal .dex extension (or a proper archive suffix) before constructing Dex.
- Feed the bytes in via new Dex(InputStream/byte[]) to bypass name-based dispatch entirely.
- Check the exact filename string at runtime (case-sensitive) if the path is composed dynamically.
Example fix
// before Dex dex = new Dex(stagedFile); // stagedFile = "patch.0352.tmp" // after File renamed = new File(stagedFile.getParentFile(), stagedFile.getName() + ".dex"); Files.copy(stagedFile.toPath(), renamed.toPath(), StandardCopyOption.REPLACE_EXISTING); Dex dex = new Dex(renamed);
Defensive patterns
Strategy: validation
Validate before calling
String n = file.getName().toLowerCase(Locale.ROOT);
if (!(n.endsWith(".dex") || n.endsWith(".zip") || n.endsWith(".jar") || n.endsWith(".apk"))) {
throw new IllegalArgumentException("unsupported dex input name: " + n);
} Prevention
- Normalize temp artifacts to their final .dex/.apk name before parsing.
- Remember extension checks here are case-sensitive; lowercase the name when composing paths dynamically.
When it happens
Trigger: new Dex(new File("classes_dex.bak")), renamed artifacts, uppercase extensions (.DEX — hasArchiveSuffix/.endsWith are case-sensitive), or temp files whose suffix was changed mid-pipeline.
Common situations: Build pipelines that stage files under temporary names (foo.dex.tmp) and pass them before renaming; case-mismatched extensions on case-insensitive filesystems; files downloaded with the wrong suffix.
Related errors
- file is null.
- Expected ${DEX_IN_JAR_NAME} in ${file}
- Unexpected type: ${type}
- invalid LEB128 sequence
- bad second byte
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/756d8c33abdcfec4.
Report an issue: GitHub.