{"record":{"id":"996290c39d6d144f","repo":"laurent22/joplin","slug":"mode-access-is-needed-for-id","errorCode":null,"errorMessage":"${mode} access is needed for ${id}.","messagePattern":"(.+?) access is needed for (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.ts","lineNumber":517,"sourceCode":"\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async md5File(path: string): Promise<string> {\n\t\tconst fileData = Buffer.from(await (await this.fileAtPath(path)).arrayBuffer());\n\t\treturn md5(fileData);\n\t}\n\n\tpublic async createReadOnlyVirtualFile(path: string, content: File) {\n\t\tthis.virtualFiles_.set(normalize(path), content);\n\t}\n\n\tpublic async mountExternalDirectory(handle: FileSystemDirectoryHandle, id: string, mode: AccessMode) {\n\t\tif (await handle.requestPermission({ mode }) !== 'granted') {\n\t\t\tthrow new Error(`${mode} access is needed for ${id}.`);\n\t\t}\n\n\t\tconst mountPath = resolve(externalDirectoryPrefix, crypto.randomUUID().replace(/-/g, ''));\n\t\tthis.externalHandles_.set(mountPath, handle);\n\n\t\tawait this.accessHandleDatabase_.clearExternalHandle(id);\n\t\tawait this.accessHandleDatabase_.addExternalHandle(mountPath, id, handle, mode);\n\n\t\treturn mountPath;\n\t}\n}\n\ninterface RemoteApi { }\nnew WorkerToWindowMessenger<WorkerApi, RemoteApi>('fs-worker', new WorkerApi());\n","sourceCodeStart":499,"sourceCodeEnd":532,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.ts#L499-L532","documentation":"Thrown by the web-worker RN fs-driver's `mountExternalDirectory` when `handle.requestPermission({ mode })` does not return 'granted' for the requested access mode (`read` or `readwrite`). The mode and the mount id are interpolated so the caller knows which mount failed and what access it needed.","triggerScenarios":"Calling `mountExternalDirectory(handle, id, mode)` where the user denies the browser permission prompt, or the handle's state was already 'denied' so the prompt cannot be shown again.","commonSituations":"User clicked 'Block' on the permission prompt; the site previously had its permission revoked at browser level; requesting 'readwrite' when the underlying file system is read-only.","solutions":["Prompt the user to allow access and retry; if previously denied, instruct them to clear site permissions first.","Fall back to requesting 'read' mode if read-write is refused and write is not strictly required.","Pre-check with `handle.queryPermission({ mode })` and only call `requestPermission` when state is 'prompt'.","Handle the error in the UI and offer to pick a different folder."],"exampleFix":"// before\nawait driver.mountExternalDirectory(handle, id, 'readwrite');\n\n// after\nif (await handle.queryPermission({ mode }) === 'prompt') {\n  if (await handle.requestPermission({ mode }) !== 'granted') {\n    throw new Error(`${mode} access is needed for ${id}.`);\n  }\n}\nawait driver.mountExternalDirectory(handle, id, mode);","handlingStrategy":"validation","validationCode":"if (await handle.queryPermission({ mode }) === 'prompt') {\n  if (await handle.requestPermission({ mode }) !== 'granted') {\n    throw new Error(`${mode} access is needed for ${id}.`);\n  }\n}","typeGuard":"const isAccessMode = (m: unknown): m is 'read' | 'readwrite' =>\n  m === 'read' || m === 'readwrite';","tryCatchPattern":"try {\n  await driver.mountExternalDirectory(handle, id, mode);\n} catch (error) {\n  if (/access is needed for/i.test(error.message)) {\n    await showPermissionDeniedHelp(id, mode);\n    return;\n  }\n  throw error;\n}","preventionTips":["Call queryPermission before requestPermission to avoid useless prompts after a denial.","Fall back to read-only when readwrite is refused and write is optional.","On persistent denial, instruct the user to reset site permissions."],"tags":["filesystem","web","permissions","file-system-access","mobile"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}