Describe the bug
When rebuilding with -dex-lib = internal (the default), the smali assembler accepts nested (overlapping) .catch try-regions and emits the overlapping ranges verbatim as try_items. The DEX spec requires try items within a code_item to be sorted, in-range and non-overlapping — so the produced dex is invalid.
Official smali (baksmali/smali, also used by apktool) supports nested .catch by flattening overlaps into disjoint try ranges with merged handler sets. APKEditor's internal assembler does neither: the build succeeds with zero warnings ("Saved to: …") and the invalid dex is shipped. On device, ART then refuses to open the whole dex file, so every class in that dex becomes unresolvable at runtime (NoClassDefFoundError on a perfectly valid, unrelated class). The d command happily disassembles such a file back — nothing in the toolchain ever complains.
To Reproduce
Steps to reproduce the behavior:
- Used version
APKEditor 1.4.9, ARSCLib 1.3.9 and 1.4.0 (both affected), OpenJDK 26.0.1+8-34, device Android 13
- Operating system
Windows 11 x64
- Command — see attached
apkeditor-nestedcatch-repro.zip (includes prebuilt nested.apk / flat.apk / nested_jf.apk):
build_repro.bat <path-to-apkeditor.jar>
It builds two ready-made mini projects (identical semantics, different catch layout) and validates the produced dexes with dexcheck.py (standalone, no deps):
mini_nested/ — one class, one method, outer .catch region fully containing an inner one (legal smali):
:try_outer
invoke-static {p0}, Landroid/graphics/Color;->parseColor(Ljava/lang/String;)I
...
:try_inner
invoke-static {p1}, Ljava/lang/Float;->parseFloat(Ljava/lang/String;)F
:try_end_inner
.catch Ljava/lang/Throwable; {:try_inner .. :try_end_inner} :inner_label
:inner_label
...
:try_end_outer
.catch Ljava/lang/Throwable; {:try_outer .. :try_end_outer} :outer_label
mini_flat/ — same code split into two disjoint regions (the workaround).
Observed output:
=== nested ===
00.465 I: [BUILD] Saved to: nested.apk (success, no diagnostic)
PROBLEMS (1):
- LRepro;#0: try overlap/unsorted start=0 prev_end=13
=== flat ===
00.466 I: [BUILD] Saved to: flat.apk
STRUCTURE OK
Log/Stacktrace
Installing the nested variant's real-world counterpart (same construct inside an app's own class) on an Android 13 device:
FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: Failed resolution of: Lf/a/a/a/a;
at androidx.preference.PreferenceManager.getDefaultSharedPreferencesName(SourceFile:1)
at androidx.preference.PreferenceManager.getDefaultSharedPreferences(SourceFile:1)
...
Caused by: java.lang.ClassNotFoundException: f.a.a.a.a
The class is present in the rebuilt dex's class_defs table (verified by direct dex parsing); the whole dex is simply rejected by ART because of the invalid try-item table. Replacing the nested regions with disjoint ones makes the identical app work.
Used apk file
Not shareable (proprietary, 169 MB). However the issue is fully reproducible without it — see the attached minimal reproduction project apkeditor-nestedcatch-repro.zip (~16 KB: mini projects, prebuilt apks, validator, one-click script, README).
Additional context
- Disjoint side-by-side
.catch regions, and multiple handlers over one region, are handled fine — only genuinely nested/overlapping ranges produce the corrupt output.
- The tool's own
-dex-lib jf (JesusFreke/smali) backend assembles the identical mini_nested source into 3 correctly flattened, non-overlapping try_items (STRUCTURE OK). So the expected behavior is implemented inside the same codebase; only the default internal assembler emits the overlap silently.
- Suggested fix: at minimum hard-error on overlapping catch regions (naming class/method/labels); ideally flatten them like official smali does.
apkeditor-nestedcatch-repro.zip
Describe the bug
When rebuilding with
-dex-lib = internal(the default), the smali assembler accepts nested (overlapping).catchtry-regions and emits the overlapping ranges verbatim astry_items. The DEX spec requires try items within acode_itemto be sorted, in-range and non-overlapping — so the produced dex is invalid.Official smali (baksmali/smali, also used by apktool) supports nested
.catchby flattening overlaps into disjoint try ranges with merged handler sets. APKEditor's internal assembler does neither: the build succeeds with zero warnings ("Saved to: …") and the invalid dex is shipped. On device, ART then refuses to open the whole dex file, so every class in that dex becomes unresolvable at runtime (NoClassDefFoundErroron a perfectly valid, unrelated class). Thedcommand happily disassembles such a file back — nothing in the toolchain ever complains.To Reproduce
Steps to reproduce the behavior:
APKEditor 1.4.9, ARSCLib1.3.9and1.4.0(both affected), OpenJDK26.0.1+8-34, device Android 13Windows 11 x64apkeditor-nestedcatch-repro.zip(includes prebuiltnested.apk/flat.apk/nested_jf.apk):It builds two ready-made mini projects (identical semantics, different catch layout) and validates the produced dexes with
dexcheck.py(standalone, no deps):mini_nested/— one class, one method, outer.catchregion fully containing an inner one (legal smali)::try_outer invoke-static {p0}, Landroid/graphics/Color;->parseColor(Ljava/lang/String;)I ... :try_inner invoke-static {p1}, Ljava/lang/Float;->parseFloat(Ljava/lang/String;)F :try_end_inner .catch Ljava/lang/Throwable; {:try_inner .. :try_end_inner} :inner_label :inner_label ... :try_end_outer .catch Ljava/lang/Throwable; {:try_outer .. :try_end_outer} :outer_labelmini_flat/— same code split into two disjoint regions (the workaround).Observed output:
Log/Stacktrace
Installing the nested variant's real-world counterpart (same construct inside an app's own class) on an Android 13 device:
The class is present in the rebuilt dex's
class_defstable (verified by direct dex parsing); the whole dex is simply rejected by ART because of the invalid try-item table. Replacing the nested regions with disjoint ones makes the identical app work.Used apk file
Not shareable (proprietary, 169 MB). However the issue is fully reproducible without it — see the attached minimal reproduction project
apkeditor-nestedcatch-repro.zip(~16 KB: mini projects, prebuilt apks, validator, one-click script, README).Additional context
.catchregions, and multiple handlers over one region, are handled fine — only genuinely nested/overlapping ranges produce the corrupt output.-dex-lib jf(JesusFreke/smali) backend assembles the identicalmini_nestedsource into 3 correctly flattened, non-overlapping try_items (STRUCTURE OK). So the expected behavior is implemented inside the same codebase; only the defaultinternalassembler emits the overlap silently.apkeditor-nestedcatch-repro.zip