Issue Type / 问题类型
Launcher Problem / 启动器问题
Issue Description / 问题描述
ENOENT when saving control layouts on Android 16
Important note: this investigation and fix were performed by an AI coding agent
I want to be transparent that the investigation, source-code analysis, diagnosis, proposed solution, code modifications, build process, and verification described in this report were performed by an AI coding agent.
I provided the affected device, reproduced the issue, collected ADB/logcat output, and tested the resulting patched APK on the actual device.
The AI agent inspected the ZalithLauncher2 source code, traced the file-saving path, identified the likely cause, implemented the fix, rebuilt the launcher from source, installed it on the affected device, and verified that the problem was resolved.
I am reporting the AI's analysis and the resulting fix here so that the maintainers can review it and potentially implement the fix properly upstream.
Environment
- Device: Xiaomi Redmi 9T (
M2010J19SY)
- OS: LunarOS
- Android: 16
- API level: 36
- Architecture: ARM64 / arm64-v8a
- Zalith Launcher: 2.4.11
- Root: Magisk
- Package:
com.movtery.zalithlauncher.v2
Problem
I could not save custom control layouts.
When trying to save a layout, Zalith Launcher reported:
Failed to save control layout
/storage/emulated/0/Android/data/com.movtery.zalithlauncher.v2/files/control_layouts/IdYe2Nxr.json:
open failed: ENOENT (No such file or directory)
The strange part was that the directory actually existed.
I verified it through ADB:
/storage/emulated/0/Android/data/com.movtery.zalithlauncher.v2/files/control_layouts
The directory had:
drwxrwx--- u0_a389 everybody
I also tested writing to the same directory through root, and root could successfully create files there.
I had already granted:
through AppOps, but this did not solve the problem.
The problem also appeared with multiple Android Minecraft launchers on this device, which initially made me suspect an Android 16 storage/scoped-storage interaction.
Investigation performed by the AI agent
The AI agent searched through the ZalithLauncher2 source code and traced the control-layout save operation.
The relevant path was approximately:
ControlManager
-> saveControl()
-> saveToFile()
-> File.writeText()
The central issue found was that saveToFile() directly attempted to write the file without ensuring that the parent directory existed.
The directory was normally created during launcher initialization, but the save operation itself did not guarantee that the directory was still present and usable.
This can result in:
when the parent path is unavailable from the application's Java filesystem view.
The AI agent also found a fallback in PathManager that manually constructed the external storage path using:
Environment.getExternalStorageDirectory()
and then appended the /Android/data/... path.
The agent considered this problematic on modern Android versions and replaced it with the application-provided external files directory.
Fix implemented by the AI agent
1. Create the parent directory before writing
In:
LayerController/.../utils/Utils.kt
the save operation was changed to ensure that the parent directory exists before calling writeText():
file.parentFile?.mkdirs()
This prevents the save operation from relying on a directory that may no longer exist.
2. Use the Android application-specific external directory
The path handling in PathManager.kt was changed from manually constructing /storage/emulated/0/Android/data/... to using:
context.getExternalFilesDir(null)
with a fallback to the application's internal filesDir.
3. Added explicit control-layout directory validation
The AI agent added an ensureControlLayoutsDir() mechanism which:
- checks whether
control_layouts exists;
- recreates it when necessary;
- checks that it is actually a directory;
- checks whether it can be written to;
- provides a fallback to internal application storage if the external location is unavailable.
4. Protected all control-layout operations
The same directory checks were added around the relevant operations in:
ControlManager.kt
ControlManageScreen.kt
EditorViewModel.kt
including:
- creating a new layout;
- saving a layout;
- importing a layout;
- unpacking the default layout.
The existing JSON format, layout IDs, loading logic, and deletion logic were not changed.
Verification
The AI agent built a modified ARM64 version of ZalithLauncher2 from source and installed it on the affected Redmi 9T.
Before the fix:
After the fix, I tested the launcher directly on the same device.
The following now work correctly:
- creating a control layout;
- editing the layout;
- saving the layout;
- changing the layout;
- restarting Zalith Launcher;
- loading the saved layout;
- creating the JSON file inside
control_layouts.
The resulting files were successfully created in:
/storage/emulated/0/Android/data/com.movtery.zalithlauncher.v2.debug/files/control_layouts/
and no ENOENT error occurred.
So the proposed fix was not only theoretical/static analysis — the AI-generated source modification was compiled and tested on the actual affected device.
Root access
The device is rooted with Magisk.
Root was used only during the investigation to compare root-level filesystem access with the access available to the launcher application.
The final fix does not use root, su, or root permissions.
The launcher works normally with the patched filesystem/path handling.
This is important because simply granting root access would only be a workaround rather than a proper fix for the launcher.
Build information
The modified launcher was built from source using:
JDK 17
Android SDK
NDK 25.2.9519653
Gradle 9.5.0
AGP 9.3.0
ARM64 / arm64-v8a
The AI agent successfully produced both debug and release builds and tested the patched launcher on the device.
The release build also avoided the 16 KB alignment warnings seen in the unoptimized debug build.
Conclusion
This appears to be an Android 16 storage/path handling issue affecting the control-layout saving code.
The AI coding agent was able to reproduce the issue from the available information, trace it to the file/path handling code, implement a fix, build the launcher, and verify the fix on the affected device.
I am submitting this report primarily so the maintainers can review the proposed source changes and determine whether a similar fix should be integrated into ZalithLauncher2 upstream.
The complete diagnosis and patch were produced by an AI coding agent; I only provided the environment, reproduction, diagnostic output, and final device testing.
Log File / 日志文件
Failed to save control layout
/storage/emulated/0/Android/data/com.movtery.zalithlauncher.v2/files/control_layouts/IdYe2Nxr.json:
open failed: ENOENT (No such file or directory)
Screenshots or Videos / 截图或视频
No response
Reproduction Steps / 复现步骤
1. Install Zalith Launcher 2.4.11 on an Android 16 device.
2. Open the launcher.
3. Open the control layout editor.
4. Create a new custom control layout or edit an existing one.
5. Try to save the layout.
6. The launcher fails to save the JSON file and reports an `ENOENT (No such file or directory)` error.
7. The `control_layouts` directory exists on the filesystem, but the launcher is still unable to complete the file write.
Android Version / Android 版本
16
Launcher Version / 启动器版本
2.4.11
Device Information / 设备信息
Device: Xiaomi Redmi 9T Model: M2010J19SY OS: LunarOS Android: 16 API level: 36 Architecture: ARM64 / arm64-v8a Root: Magisk
Issue Type / 问题类型
Launcher Problem / 启动器问题
Issue Description / 问题描述
ENOENT when saving control layouts on Android 16
Important note: this investigation and fix were performed by an AI coding agent
I want to be transparent that the investigation, source-code analysis, diagnosis, proposed solution, code modifications, build process, and verification described in this report were performed by an AI coding agent.
I provided the affected device, reproduced the issue, collected ADB/logcat output, and tested the resulting patched APK on the actual device.
The AI agent inspected the ZalithLauncher2 source code, traced the file-saving path, identified the likely cause, implemented the fix, rebuilt the launcher from source, installed it on the affected device, and verified that the problem was resolved.
I am reporting the AI's analysis and the resulting fix here so that the maintainers can review it and potentially implement the fix properly upstream.
Environment
M2010J19SY)com.movtery.zalithlauncher.v2Problem
I could not save custom control layouts.
When trying to save a layout, Zalith Launcher reported:
The strange part was that the directory actually existed.
I verified it through ADB:
The directory had:
I also tested writing to the same directory through root, and root could successfully create files there.
I had already granted:
through AppOps, but this did not solve the problem.
The problem also appeared with multiple Android Minecraft launchers on this device, which initially made me suspect an Android 16 storage/scoped-storage interaction.
Investigation performed by the AI agent
The AI agent searched through the ZalithLauncher2 source code and traced the control-layout save operation.
The relevant path was approximately:
The central issue found was that
saveToFile()directly attempted to write the file without ensuring that the parent directory existed.The directory was normally created during launcher initialization, but the save operation itself did not guarantee that the directory was still present and usable.
This can result in:
when the parent path is unavailable from the application's Java filesystem view.
The AI agent also found a fallback in
PathManagerthat manually constructed the external storage path using:Environment.getExternalStorageDirectory()and then appended the
/Android/data/...path.The agent considered this problematic on modern Android versions and replaced it with the application-provided external files directory.
Fix implemented by the AI agent
1. Create the parent directory before writing
In:
the save operation was changed to ensure that the parent directory exists before calling
writeText():This prevents the save operation from relying on a directory that may no longer exist.
2. Use the Android application-specific external directory
The path handling in
PathManager.ktwas changed from manually constructing/storage/emulated/0/Android/data/...to using:context.getExternalFilesDir(null)with a fallback to the application's internal
filesDir.3. Added explicit control-layout directory validation
The AI agent added an
ensureControlLayoutsDir()mechanism which:control_layoutsexists;4. Protected all control-layout operations
The same directory checks were added around the relevant operations in:
including:
The existing JSON format, layout IDs, loading logic, and deletion logic were not changed.
Verification
The AI agent built a modified ARM64 version of ZalithLauncher2 from source and installed it on the affected Redmi 9T.
Before the fix:
After the fix, I tested the launcher directly on the same device.
The following now work correctly:
control_layouts.The resulting files were successfully created in:
and no ENOENT error occurred.
So the proposed fix was not only theoretical/static analysis — the AI-generated source modification was compiled and tested on the actual affected device.
Root access
The device is rooted with Magisk.
Root was used only during the investigation to compare root-level filesystem access with the access available to the launcher application.
The final fix does not use root,
su, or root permissions.The launcher works normally with the patched filesystem/path handling.
This is important because simply granting root access would only be a workaround rather than a proper fix for the launcher.
Build information
The modified launcher was built from source using:
The AI agent successfully produced both debug and release builds and tested the patched launcher on the device.
The release build also avoided the 16 KB alignment warnings seen in the unoptimized debug build.
Conclusion
This appears to be an Android 16 storage/path handling issue affecting the control-layout saving code.
The AI coding agent was able to reproduce the issue from the available information, trace it to the file/path handling code, implement a fix, build the launcher, and verify the fix on the affected device.
I am submitting this report primarily so the maintainers can review the proposed source changes and determine whether a similar fix should be integrated into ZalithLauncher2 upstream.
The complete diagnosis and patch were produced by an AI coding agent; I only provided the environment, reproduction, diagnostic output, and final device testing.
Log File / 日志文件
Failed to save control layout
/storage/emulated/0/Android/data/com.movtery.zalithlauncher.v2/files/control_layouts/IdYe2Nxr.json:
open failed: ENOENT (No such file or directory)
Screenshots or Videos / 截图或视频
No response
Reproduction Steps / 复现步骤
Android Version / Android 版本
16
Launcher Version / 启动器版本
2.4.11
Device Information / 设备信息
Device: Xiaomi Redmi 9T Model: M2010J19SY OS: LunarOS Android: 16 API level: 36 Architecture: ARM64 / arm64-v8a Root: Magisk