{"record":{"id":"10691666622316dc","repo":"MuntashirAkon/AppManager","slug":"shared-storage-unavailable","errorCode":null,"errorMessage":"Shared storage unavailable.","messagePattern":"Shared storage unavailable\\.","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java","lineNumber":187,"sourceCode":"        Context context = ContextUtils.getContext();\n        try {\n            return getExternalCachePath(context);\n        } catch (FileNotFoundException e) {\n            return context.getCacheDir();\n        }\n    }\n\n    @AnyThread\n    @NonNull\n    public static File getExternalCachePath(@NonNull Context context) throws FileNotFoundException {\n        return getBestExternalDataSubdir(context.getExternalCacheDirs());\n    }\n\n    @AnyThread\n    @NonNull\n    public static File getBestExternalDataSubdir(@Nullable File[] extDirs) throws FileNotFoundException {\n        if (extDirs == null) {\n            throw new FileNotFoundException(\"Shared storage unavailable.\");\n        }\n        String lastReason = null;\n        for (File extDir : extDirs) {\n            // The priority is from top to bottom of the list as per Context#getExternalDir()\n            if (extDir == null) {\n                // Other external directory might exist\n                continue;\n            }\n            if (!(extDir.exists() || extDir.mkdirs())) {\n                // Try to recreate this with root\n                if (RunnerUtils.isRootGiven() && forceCreateExternalDataSubDir(extDir)) {\n                    Log.i(TAG, \"Root created %s\", extDir);\n                    return extDir;\n                }\n                lastReason = extDir + \": permission denied.\";\n                Log.w(TAG, \"Could not use %s.\", extDir);\n                continue;\n            }","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java#L169-L205","documentation":"getBestExternalDataSubdir scans the device's array of app-specific external storage directories and returns the first usable one. It throws FileNotFoundException(\"Shared storage unavailable.\") when the extDirs array itself is null, meaning the OS reported no external/shared storage at all (Context#getExternalFilesDirs returned null). The method never returns null; callers like getExternalCachePath propagate this failure.","triggerScenarios":"Calling getExternalCachePath/getBestExternalDataSubdir on a device where Context#getExternalFilesDirs(String, String) returns null — typically because no external/shared volume is mounted or accessible to the app (rare on modern Android; occurs with inaccessible storage profiles, restricted storage permissions, or unusual builds).","commonSituations":"Devices with emulated storage removed or unmounted; work-profile/secondary-user setups where external dirs are disabled; custom ROMs or emulators with broken storage emulation; running very early in the boot cycle before volumes are scanned.","solutions":["Check extDirs != null (or catch FileNotFoundException) before relying on external storage and fall back to internal Context#getCacheDir/getFilesDir","Verify external storage is mounted via Environment.getExternalStorageState() before calling","Avoid calling storage APIs before the device finishes booting (check Environment.getExternalStorageState / boot-completed)","On restricted profiles, grant or verify storage access for the calling user"],"exampleFix":"// before\nFile dir = FileUtils.getBestExternalDataSubdir(extDirs);\n// after\nFile dir;\ntry {\n    dir = FileUtils.getBestExternalDataSubdir(extDirs);\n} catch (FileNotFoundException e) {\n    dir = context.getCacheDir(); // fall back to internal storage\n}","handlingStrategy":"fallback","validationCode":"File[] extDirs = context.getExternalFilesDirs(null);\nif (extDirs == null || Arrays.stream(extDirs).filter(Objects::nonNull).findAny().isEmpty()) {\n    // use internal storage path\n}","typeGuard":"boolean hasExternalStorage(File[] extDirs) {\n    return extDirs != null && extDirs.length > 0 && Arrays.stream(extDirs).anyMatch(Objects::nonNull);\n}","tryCatchPattern":"try {\n    dir = FileUtils.getBestExternalDataSubdir(extDirs);\n} catch (FileNotFoundException e) {\n    dir = context.getCacheDir();\n}","preventionTips":["Check Environment.getExternalStorageState() before touching external storage","Always pair external-storage use with an internal-storage fallback","Avoid storage calls before boot completes"],"tags":["android","storage","file-not-found"],"backgroundTag":"file-not-found","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}