diff --git a/tools/cli/installers/lib/ide/_config-driven.js b/tools/cli/installers/lib/ide/_config-driven.js index 6e8c7c8a0..d1552700f 100644 --- a/tools/cli/installers/lib/ide/_config-driven.js +++ b/tools/cli/installers/lib/ide/_config-driven.js @@ -557,14 +557,21 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} if (!fullPath.startsWith(resolvedProject + path.sep) && fullPath !== resolvedProject) break; try { if (!(await fs.pathExists(fullPath))) { + // Dir already gone — advance current; last is reset at top of next iteration current = path.dirname(current); continue; } const remaining = await fs.readdir(fullPath); if (remaining.length > 0) break; await fs.rmdir(fullPath); - } catch { - break; + } catch (error) { + // ENOTEMPTY: TOCTOU race (file added between readdir and rmdir) — skip level, continue upward + // ENOENT: dir removed by another process between pathExists and rmdir — skip level, continue upward + if (error.code === 'ENOTEMPTY' || error.code === 'ENOENT') { + current = path.dirname(current); + continue; + } + break; // fatal error (e.g. EACCES) — stop upward walk } current = path.dirname(current); }