{"record":{"id":"4c03a462f014a1f9","repo":"MuntashirAkon/AppManager","slug":"no-available-shared-storage-found","errorCode":null,"errorMessage":"No available shared storage found.","messagePattern":"No available shared storage found\\.","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java","lineNumber":214,"sourceCode":"            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            }\n            String storageState = Environment.getExternalStorageState(extDir);\n            if (!Objects.equals(storageState, Environment.MEDIA_MOUNTED)) {\n                lastReason = extDir + \": not mounted (\" + storageState + \")\";\n                Log.w(TAG, \"Path %s not mounted. State: %s\", extDir, storageState);\n                continue;\n            }\n            return extDir;\n        }\n        throw new FileNotFoundException(lastReason != null ? lastReason : \"No available shared storage found.\");\n    }\n\n    public static boolean forceCreateExternalDataSubDir(@NonNull File dir) {\n        File parentFile = Objects.requireNonNull(dir.getParentFile());\n        String parent = parentFile.getAbsolutePath();\n        String target = dir.getAbsolutePath();\n        String chownTarget;\n        boolean createParent = !parentFile.exists();\n        if (createParent) {\n            // Some rooted Android ROMs cannot create the package directory under Android/data.\n            int uid = Process.myUid();\n            chownTarget = uid + \":\" + uid;\n        } else {\n            try {\n                StructStat parentStat = Os.stat(parent);\n                chownTarget = parentStat.st_uid + \":\" + parentStat.st_gid;\n            } catch (ErrnoException e) {\n                // Fallback to shell","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java#L196-L232","documentation":"After iterating the extDirs array, getBestExternalDataSubdir throws FileNotFoundException(\"No available shared storage found.\") when no entry passed the usability checks. Each candidate is logged with a reason (e.g. '<path>: not mounted (unmounted)') and that last reason becomes the exception message when present. Unlike error 410 the array was non-null — the directories exist but none is mounted/usable.","triggerScenarios":"Calling getExternalCachePath/getBestExternalDataSubdir when every entry in getExternalFilesDirs(...) is null or whose Environment.getExternalStorageState(dir) is not Environment.MEDIA_MOUNTED (e.g. 'unmounted', 'removed', 'shared' while USB mass-storage is active).","commonSituations":"SD card ejected or unmounted while app uses it; device connected in USB mass-storage/MTP mode exposing the volume; corrupted volumes reported as unmounted; emulator snapshots with detached virtual SD card.","solutions":["Catch FileNotFoundException and fall back to internal storage (context.getCacheDir())","Log/inspect the exception message — it contains the exact path and Environment state that failed","Check Environment.getExternalStorageState() for each candidate dir before use","Prompt the user to mount/remount the storage volume or reinsert the SD card"],"exampleFix":"// before\nFile extDir = FileUtils.getBestExternalDataSubdir(extDirs);\n// after\ntry {\n    extDir = FileUtils.getBestExternalDataSubdir(extDirs);\n} catch (FileNotFoundException e) {\n    Log.w(TAG, \"External storage unavailable: \" + e.getMessage());\n    extDir = context.getCacheDir();\n}","handlingStrategy":"fallback","validationCode":"for (File d : extDirs) {\n    if (d != null && Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(d))) {\n        // usable dir found\n    }\n}","typeGuard":"boolean isMounted(File dir) {\n    return dir != null && Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(dir));\n}","tryCatchPattern":"try {\n    dir = FileUtils.getBestExternalDataSubdir(extDirs);\n} catch (FileNotFoundException e) {\n    Log.w(TAG, \"No usable external storage: \" + e.getMessage());\n    dir = context.getCacheDir();\n}","preventionTips":["Log the exception message — it names the exact failing path and mount state","Handle SD-card ejection events (storage state broadcasts) proactively","Never assume the first external dir is mounted"],"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"}