From e8565a08d4cb413e984087936f20beb87c490f84 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Wed, 12 Aug 2026 20:38:16 +0530 Subject: [PATCH] fix(afc): make walk tolerant to untraversable directories --- src/services/ios/afc/index.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/services/ios/afc/index.ts b/src/services/ios/afc/index.ts index a16c9ca8..264b19c6 100644 --- a/src/services/ios/afc/index.ts +++ b/src/services/ios/afc/index.ts @@ -479,14 +479,33 @@ export class AfcService { log.debug(`Successfully pushed file to '${remoteDst}'`); } + /** + * Recursively list `root` and everything below it. Untraversable directories are skipped + * rather than aborting the walk: the media sandbox exposes directories it refuses to read + * (e.g. `/PhotoData/UBF` answers GET_FILE_INFO but fails READ_DIR with PERM_DENIED). + */ async walk(root: string): Promise> { const out: Array<{dir: string; dirs: string[]; files: string[]}> = []; - const entries = await this.listdir(root); + let entries: string[]; + try { + entries = await this.listdir(root); + } catch (error) { + log.debug(`Skipping '${root}' during walk, cannot list it:`, error); + return out; + } const dirs: string[] = []; const files: string[] = []; for (const e of entries) { const p = path.posix.join(root, e); - if (await this.isdir(p)) { + let isDir: boolean; + try { + isDir = await this.isdir(p); + } catch (error) { + // Unstattable entry: report it, but do not try to descend into it. + log.debug(`Cannot stat '${p}' during walk, treating it as a file:`, error); + isDir = false; + } + if (isDir) { dirs.push(e); } else { files.push(e);