{"record":{"id":"8d90fbcbd59bbc96","repo":"maotoumao/MusicFree","slug":"toast-foldernotexistornopermission","errorCode":null,"errorMessage":"toast.folderNotExistOrNoPermission","messagePattern":"toast\\.folderNotExistOrNoPermission","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/pages/setting/settingTypes/basicSetting.tsx","lineNumber":366,"sourceCode":"                                pathConst.downloadMusicPath}\r\n                        </ThemeText>\r\n                    ),\r\n                    onPress() {\r\n                        navigate<\"file-selector\">(ROUTE_PATH.FILE_SELECTOR, {\r\n                            fileType: \"folder\",\r\n                            multi: false,\r\n                            actionText: t(\"basicSettings.fileSelector.selectFolder\"),\r\n                            async onAction(selectedFiles) {\r\n                                try {\r\n                                    const targetDir = selectedFiles[0];\r\n                                    await readdir(targetDir.path);\r\n                                    Config.setConfig(\r\n                                        \"basic.downloadPath\",\r\n                                        targetDir.path,\r\n                                    );\r\n                                    return true;\r\n                                } catch {\r\n                                    Toast.warn(t(\"toast.folderNotExistOrNoPermission\"));\r\n                                    return false;\r\n                                }\r\n                            },\r\n                        });\r\n                    },\r\n                },\r\n                createRadio(\r\n                    t(\"basicSettings.maxDownload\"),\r\n                    \"basic.maxDownload\",\r\n                    [1, 3, 5, 7],\r\n                    maxDownload ?? 3,\r\n                ),\r\n                createRadio(\r\n                    t(\"basicSettings.defaultDownloadQuality\"),\r\n                    \"basic.defaultDownloadQuality\",\r\n                    qualityKeys,\r\n                    defaultDownloadQuality ?? \"standard\",\r\n                    {\r","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/maotoumao/MusicFree/blob/d118b18b3d0c904400f7eea7bf99c0ceec6c1aee/src/pages/setting/settingTypes/basicSetting.tsx#L348-L384","documentation":"Warning toast in the download-path picker's onAction callback (basicSetting.tsx:366). After the user selects a folder, the code calls readdir(targetDir.path) to validate it is actually readable; if that promise rejects, the folder is treated as non-existent or permission-denied and the selection is rejected (returns false) without saving basic.downloadPath.","triggerScenarios":"readdir() on the chosen path throws because the directory was deleted/moved between selection and validation, it sits on unmounted storage (SD card, USB OTG), or Android storage permissions (READ/WRITE_EXTERNAL_STORAGE or SAF grants) are missing for that location.","commonSituations":"Selecting a folder on a removable SD card that was unmounted; picking a path under /storage/emulated/0 without storage permission granted in Android settings; directory removed by another app or a file manager after selection; app not granted 'All files access' for restricted paths.","solutions":["Grant the app storage permission (Settings > Apps > MusicFree > Permissions, or All files access on Android 11+)","Verify the folder exists and storage is mounted (re-plug/remount SD card), then pick it again","Choose a folder in app-accessible storage (e.g. /storage/emulated/0/Music) instead of a restricted/rooted path","Restart the app to refresh SAF permission grants, then retry the selection"],"exampleFix":"// before\ntry {\n    const targetDir = selectedFiles[0];\n    await readdir(targetDir.path);\n    Config.setConfig(\"basic.downloadPath\", targetDir.path);\n    return true;\n} catch {\n    Toast.warn(t(\"toast.folderNotExistOrNoPermission\"));\n    return false;\n}\n// after\ntry {\n    const targetDir = selectedFiles[0];\n    await mkdir(targetDir.path); // no-op if it exists, creates if missing\n    await readdir(targetDir.path);\n    Config.setConfig(\"basic.downloadPath\", targetDir.path);\n    return true;\n} catch (e: any) {\n    Toast.warn(e?.code === \"EACCES\"\n        ? t(\"toast.noStoragePermission\")\n        : t(\"toast.folderNotExistOrNoPermission\"));\n    return false;\n}","handlingStrategy":"try-catch","validationCode":"import { PermissionsAndroid } from \"react-native\";\nconst granted = await PermissionsAndroid.request(\n    PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,\n);\nif (granted !== PermissionsAndroid.RESULTS.GRANTED) return false;\nawait readdir(targetDir.path); // fails -> folder missing or no permission","typeGuard":null,"tryCatchPattern":"try {\n    await readdir(targetDir.path);\n    Config.setConfig(\"basic.downloadPath\", targetDir.path);\n    return true;\n} catch {\n    Toast.warn(t(\"toast.folderNotExistOrNoPermission\"));\n    return false;\n}","preventionTips":["Grant storage/All-files-access permission before picking a download folder","Avoid picking folders on removable or recently unmounted storage","Prefer app-accessible directories (e.g. /storage/emulated/0/Music)","Re-select the folder after app updates that reset permission grants"],"tags":["filesystem","permissions","android-storage","folder-validation"],"backgroundTag":"directory-not-writable","analyzedSha":"d118b18b3d0c904400f7eea7bf99c0ceec6c1aee","analyzedAt":"2026-08-30T11:49:12.932Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}