AvaloniaUI/Avalonia · error · Error
Permissions denied
Error message
Permissions denied
What it means
Thrown by StorageItem.verifyPermissions when handle.requestPermission({mode}) resolves to 'denied'. After queryPermission is not 'granted', the code requests permission; if the user denies the browser permission prompt (or a prior denial is sticky), this error aborts the operation.
Source
Thrown at src/Browser/Avalonia.Browser/webapp/modules/storage/storageItem.ts:181
return await ((item.handle as any).move(destination /*, newName */) as Promise<any>);
}
private async verifyPermissions(mode: "read" | "readwrite"): Promise<void | never> {
if (!this.handle) {
return;
}
// If we are using polyfill, let it decide permissions by itself, we can't request anything in this case.
if (!Caniuse.hasNativeFilePicker()) {
return;
}
if (await this.handle.queryPermission({ mode }) === "granted") {
return;
}
if (await this.handle.requestPermission({ mode }) === "denied") {
throw new Error("Permissions denied");
}
}
public static async saveBookmark(item: StorageItem): Promise<string | null> {
// If file was previously bookmarked, just return old one.
if (item.bookmarkId) {
return item.bookmarkId;
}
// Bookmarks are not supported with polyfill.
if (!item.handle || !Caniuse.hasNativeFilePicker()) {
return null;
}
const connection = await avaloniaDb.connect();
try {
const key = await connection.put(fileBookmarksStore, item.handle, item.generateBookmarkId());
return key as string;View on GitHub (pinned to 11c5427268)
Solutions
- Catch the error and surface a clear UI asking the user to grant permission, then retry.
- Check handle.queryPermission first and only call the operation when it is 'granted', requesting permission via a user gesture.
- Ensure the permission request originates from a user gesture (click), as browsers may auto-deny non-gesture requests.
- Fall back to a non-persistent path (download/upload) if permission is persistently denied.
Example fix
// before
const file = await StorageItem.openRead(item); // user denied -> throws
// after
const status = await item.handle?.queryPermission({ mode: 'read' });
if (status !== 'granted') { showPermissionPrompt(); return; }
try { const file = await StorageItem.openRead(item); }
catch (e) { if (e.message === 'Permissions denied') showRetryUI(); else throw e; } Defensive patterns
Strategy: try-catch
Validate before calling
async function ensurePermission(handle: FileSystemHandle, mode: 'read' | 'readwrite'): Promise<boolean> {
if (await handle.queryPermission({ mode }) === 'granted') return true;
return await handle.requestPermission({ mode }) === 'granted';
} Type guard
function hasQueryablePermission(handle: any): boolean {
return !!handle && typeof handle.queryPermission === 'function';
} Try / catch
try { return await StorageItem.openRead(item); }
catch (e) {
if (e instanceof Error && e.message === 'Permissions denied') { showPermissionRetryUI(); return null; }
throw e;
} Prevention
- Request permission from a user gesture (click).
- Check queryPermission before operating; only request when not granted.
- Offer a non-persistent fallback (download/upload) if denied.
When it happens
Trigger: Any storage operation (openRead/openWrite/createFile/createFolder/moveAsync) that calls verifyPermissions('read'|'readwrite') and the user dismisses or denies the permission prompt, or requestPermission returns 'denied' because the site lacks persistence/engagement.
Common situations: User clicks 'Block' on the file-system permission prompt; the origin is in a denied state from a previous session; calling readwrite without the handle being granted; ephemeral/private browsing where prompts auto-deny.
Related errors
- StorageItem is not a file
- StorageItem is not a writeable file
- Unable to create item in the requested directory
- Unable to move item to the requested directory
- Unable to access .NET memory
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/2f0b8df073555a37.
Report an issue: GitHub.