laurent22/joplin · error · Error

${mode} access is needed for ${id}.

Error message

${mode} access is needed for ${id}.

What it means

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.

Source

Thrown at packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.ts:517

				return true;
			}

			throw error;
		}
	}

	public async md5File(path: string): Promise<string> {
		const fileData = Buffer.from(await (await this.fileAtPath(path)).arrayBuffer());
		return md5(fileData);
	}

	public async createReadOnlyVirtualFile(path: string, content: File) {
		this.virtualFiles_.set(normalize(path), content);
	}

	public async mountExternalDirectory(handle: FileSystemDirectoryHandle, id: string, mode: AccessMode) {
		if (await handle.requestPermission({ mode }) !== 'granted') {
			throw new Error(`${mode} access is needed for ${id}.`);
		}

		const mountPath = resolve(externalDirectoryPrefix, crypto.randomUUID().replace(/-/g, ''));
		this.externalHandles_.set(mountPath, handle);

		await this.accessHandleDatabase_.clearExternalHandle(id);
		await this.accessHandleDatabase_.addExternalHandle(mountPath, id, handle, mode);

		return mountPath;
	}
}

interface RemoteApi { }
new WorkerToWindowMessenger<WorkerApi, RemoteApi>('fs-worker', new WorkerApi());

View on GitHub (pinned to 2654b33620)

Solutions

  1. Prompt the user to allow access and retry; if previously denied, instruct them to clear site permissions first.
  2. Fall back to requesting 'read' mode if read-write is refused and write is not strictly required.
  3. Pre-check with `handle.queryPermission({ mode })` and only call `requestPermission` when state is 'prompt'.
  4. Handle the error in the UI and offer to pick a different folder.

Example fix

// before
await driver.mountExternalDirectory(handle, id, 'readwrite');

// after
if (await handle.queryPermission({ mode }) === 'prompt') {
  if (await handle.requestPermission({ mode }) !== 'granted') {
    throw new Error(`${mode} access is needed for ${id}.`);
  }
}
await driver.mountExternalDirectory(handle, id, mode);
Defensive patterns

Strategy: validation

Validate before calling

if (await handle.queryPermission({ mode }) === 'prompt') {
  if (await handle.requestPermission({ mode }) !== 'granted') {
    throw new Error(`${mode} access is needed for ${id}.`);
  }
}

Type guard

const isAccessMode = (m: unknown): m is 'read' | 'readwrite' =>
  m === 'read' || m === 'readwrite';

Try / catch

try {
  await driver.mountExternalDirectory(handle, id, mode);
} catch (error) {
  if (/access is needed for/i.test(error.message)) {
    await showPermissionDeniedHelp(id, mode);
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/996290c39d6d144f. Report an issue: GitHub.