Files
orca/src/main/ipc/readdir-error-diagnostics.ts
T
Brennan BensonandNeil 9078ecaef5 fix(win): record a redacted breadcrumb when fs:readDir throws (#6862)
The user report F0BDXH978GJ surfaced 'Error invoking remote method
fs:readDir' — Electron's opaque message when the main-process handler
throws. On Windows this is typically a WSL \wsl$ / \wsl.localhostUNC path or network drive failing realpath/readdir after the distro or
share goes away, or a dropped SSH provider. Nothing recorded which throw
site fired or what kind of path was involved, so the crash report
carried no actionable cause.

Wrap the fs:readDir handler to record an 'fs_readdir_error' breadcrumb
on throw, tagging the throw site (ssh-provider | authorize | readdir),
the error name/code, and a REDACTED path shape (isUNC, isWsl,
driveLetter, hasConnectionId) — never the raw path. The error is
re-thrown unchanged, so renderer behavior is identical; only crash
reports gain structured context.

Pure classification lives in readdir-error-diagnostics.ts (8 unit tests
covering WSL UNC, \wsl$, network share, drive letter, SSH, and
no-path-leak); 4 handler tests assert the breadcrumb fires per throw
site and not on success.

Co-authored-by: Neil <neil@stably.ai>
2026-06-30 00:28:07 -07:00

58 lines
2.0 KiB
TypeScript

import type { CrashReportBreadcrumbData } from '../../shared/crash-reporting'
export type ReadDirThrowSite = 'ssh-provider' | 'authorize' | 'readdir'
/**
* Redacted shape of a directory path for crash diagnostics.
*
* Why shape, not the path: "Error invoking remote method 'fs:readDir'" reaches
* the renderer with no cause. We want to know *what kind* of path failed (WSL
* UNC, network share, drive letter, SSH) without recording the path itself —
* even though breadcrumbs are path-redacted downstream, never collecting the
* raw path is the safer default.
*/
export function describeReadDirPathShape(
dirPath: string,
connectionId: string | undefined
): CrashReportBreadcrumbData {
const isUNC = /^[\\/]{2}/.test(dirPath)
const lower = dirPath.toLowerCase()
// \\wsl$\ and \\wsl.localhost\ (either slash direction) are WSL UNC roots.
const isWsl = isUNC && (lower.includes('wsl$') || lower.includes('wsl.localhost'))
const driveLetterMatch = /^([a-zA-Z]):[\\/]/.exec(dirPath)
return {
hasConnectionId: Boolean(connectionId),
isUNC,
isWsl,
...(driveLetterMatch ? { driveLetter: driveLetterMatch[1].toUpperCase() } : {})
}
}
function errorCode(error: unknown): string | undefined {
if (error && typeof error === 'object' && 'code' in error) {
const code = (error as { code?: unknown }).code
if (typeof code === 'string') {
return code
}
}
return undefined
}
/**
* Build the redacted breadcrumb payload for a thrown fs:readDir call: which
* throw site fired, the error code/name, and the path shape — no raw path.
*/
export function buildReadDirErrorBreadcrumb(args: {
dirPath: string
connectionId: string | undefined
throwSite: ReadDirThrowSite
error: unknown
}): CrashReportBreadcrumbData {
return {
throwSite: args.throwSite,
errorName: args.error instanceof Error ? args.error.name : typeof args.error,
...(errorCode(args.error) ? { errorCode: errorCode(args.error)! } : {}),
...describeReadDirPathShape(args.dirPath, args.connectionId)
}
}