From 191fdf2ae6056c78bdfeff88c3ed6cd6e94b1b66 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 30 Jul 2026 00:23:24 -0700 Subject: [PATCH] fix(runtime): skip unreadable Windows drives (#11421) Co-authored-by: Orca --- .../runtime/windows-drive-listing.test.ts | 60 +++++++++++++++++-- src/main/runtime/windows-drive-listing.ts | 11 ++-- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/main/runtime/windows-drive-listing.test.ts b/src/main/runtime/windows-drive-listing.test.ts index 1d7387e7932..4ceef94bb55 100644 --- a/src/main/runtime/windows-drive-listing.test.ts +++ b/src/main/runtime/windows-drive-listing.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import type { Stats } from 'node:fs' import { isServerDriveListRequest, listWindowsDrives } from './windows-drive-listing' @@ -20,6 +20,10 @@ describe('isServerDriveListRequest', () => { }) describe('listWindowsDrives', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + const statOnly = (mounted: string[]) => async (p: string) => { if (!mounted.includes(p)) { throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) @@ -51,14 +55,62 @@ describe('listWindowsDrives', () => { expect(result.entries.map((e) => e.name)).toEqual(['C:\\']) }) - it.each(['EIO', 'EMFILE'])('surfaces systemic %s failures', async (code) => { + it('skips an unreadable drive with an unexpected errno and logs it', async () => { + const error = Object.assign(new Error('EIO'), { code: 'EIO' }) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) const statPath = async (p: string): Promise => { if (p === 'D:\\') { - throw Object.assign(new Error(code), { code }) + throw error } throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) } - await expect(listWindowsDrives(statPath)).rejects.toMatchObject({ code }) + const result = await listWindowsDrives(statPath) + + expect(result.entries).toEqual([]) + expect(warn).toHaveBeenCalledOnce() + expect(warn).toHaveBeenCalledWith('[windows-drive-listing] Failed to stat drive', { + root: 'D:\\', + error + }) + }) + + it('surfaces programming errors from isDirectory', async () => { + const error = new TypeError('programmer bug') + const statPath = async (p: string): Promise => { + if (p === 'D:\\') { + return { + isDirectory: () => { + throw error + } + } as unknown as Stats + } + throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) + } + + await expect(listWindowsDrives(statPath)).rejects.toBe(error) + }) + + it('returns healthy drives among tolerated and unexpected stat failures', async () => { + const error = Object.assign(new Error('EIO'), { code: 'EIO' }) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + const statPath = async (p: string): Promise => { + if (p === 'C:\\' || p === 'F:\\') { + return { isDirectory: () => true } as Stats + } + if (p === 'E:\\') { + throw error + } + throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) + } + + const result = await listWindowsDrives(statPath) + + expect(result.entries.map((entry) => entry.name)).toEqual(['C:\\', 'F:\\']) + expect(warn).toHaveBeenCalledOnce() + expect(warn).toHaveBeenCalledWith('[windows-drive-listing] Failed to stat drive', { + root: 'E:\\', + error + }) }) }) diff --git a/src/main/runtime/windows-drive-listing.ts b/src/main/runtime/windows-drive-listing.ts index 9b2bbcfdbc0..1b9dbac062c 100644 --- a/src/main/runtime/windows-drive-listing.ts +++ b/src/main/runtime/windows-drive-listing.ts @@ -26,15 +26,16 @@ export async function listWindowsDrives( const roots = await Promise.all( [...DRIVE_LETTERS].map(async (letter) => { const root = `${letter}:\\` + let stats: Stats try { - const stats = await statPath(root) - return stats.isDirectory() ? root : null + stats = await statPath(root) } catch (error) { - if (isExpectedUnavailableDriveError(error)) { - return null + if (!isExpectedUnavailableDriveError(error)) { + console.warn('[windows-drive-listing] Failed to stat drive', { root, error }) } - throw error + return null } + return stats.isDirectory() ? root : null }) ) return {