{"record":{"id":"777a2f26815e6af2","repo":"laurent22/joplin","slug":"could-not-read-directory-path-error-message","errorCode":null,"errorMessage":"Could not read directory: ${path}: ${error.message}","messagePattern":"Could not read directory: (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-mobile/utils/fs-driver/fs-driver-rn.ts","lineNumber":126,"sourceCode":"\t\t\tpath: path,\n\t\t\tsize: stat.size,\n\t\t};\n\t}\n\n\tpublic async readDirStats(path: string, options: ReadDirStatsOptions = null) {\n\t\tif (!options) options = { recursive: false };\n\n\t\tconst isScoped = isScopedUri(path);\n\n\t\tlet stats: RnfsStatLike[] = [];\n\t\ttry {\n\t\t\tif (isScoped) {\n\t\t\t\tstats = await RNSAF.listFiles(path);\n\t\t\t} else {\n\t\t\t\tstats = await RNFS.readDir(path);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Could not read directory: ${path}: ${error.message}`);\n\t\t}\n\n\t\tconst toRelativePath = (stat: RnfsStatLike) => {\n\t\t\tlet relativePath = isScoped ? (stat as DocumentFileDetail).uri : (stat as StatResultT | ReadDirResItemT).path;\n\n\t\t\t// Workaround: Paths returned by RNFS.readDir can include a leading /private/, when this isn't included\n\t\t\t// in the original path variable:\n\t\t\tif (relativePath.startsWith('/private/') && !path.startsWith('/private/')) {\n\t\t\t\trelativePath = relativePath.replace(/^\\/private/, '');\n\t\t\t}\n\n\t\t\tif (!relativePath.startsWith(path)) {\n\t\t\t\tlogger.warn('readDirStats: Relative path does not start with original:', { relativePath, path });\n\t\t\t}\n\n\t\t\trelativePath = relativePath.substring(path.length + 1);\n\t\t\treturn relativePath;\n\t\t};","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts#L108-L144","documentation":"Thrown by the React Native fs-driver's `readDirStats`/directory read path when the underlying `RNSAF.listFiles` (scoped storage URI) or `RNFS.readDir` (filesystem path) throws. The path and the native error's message are interpolated. It wraps any native read failure into a single, identifiable error.","triggerScenarios":"Calling a directory-read API on a path that does not exist, is not a directory, lacks permission, or whose scoped-storage handle has been revoked. Also reached if the path is malformed for the chosen backend (non-URI string passed to the scoped branch, or vice-versa).","commonSituations":"The user revoked the SAF folder permission after granting it; the directory was deleted out-of-band; an SD-card unmount; a path that mixes `/private/` prefix forms (note the workaround below this throw); Android scoped storage restrictions.","solutions":["Check existence before reading: `if (!await fsDriver.exists(path)) return [];`.","If using a scoped URI, ensure the handle was granted via `mountExternalDirectory` and not revoked.","Re-prompt the user to re-share the folder when the SAF permission was revoked.","Handle the `/private/` prefix mismatch by normalizing paths before comparison."],"exampleFix":"// before\nconst stats = await shim.fsDriver().readDirStats(path);\n\n// after\nif (!await shim.fsDriver().exists(path)) return [];\ntry {\n  return await shim.fsDriver().readDirStats(path);\n} catch (error) {\n  logger.warn('Directory read failed, may need re-share:', path, error.message);\n  return [];\n}","handlingStrategy":"try-catch","validationCode":"if (!await shim.fsDriver().exists(path)) {\n  logger.info('Directory does not exist:', path);\n  return [];\n}","typeGuard":"null","tryCatchPattern":"try {\n  return await shim.fsDriver().readDirStats(path);\n} catch (error) {\n  if (/Could not read directory/i.test(error.message)) {\n    logger.warn('Read failed; may need to re-share folder:', path);\n    return [];\n  }\n  throw error;\n}","preventionTips":["Check directory existence before reading.","Re-share scoped-storage folders when SAF permission is revoked.","Normalize /private/ prefix mismatches before path comparison."],"tags":["filesystem","mobile","scoped-storage","rnfs","permissions"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}