Skip to content

Commit 3d6c7ef

Browse files
committed
🤖 Fix double-close error by using 'using' pattern for directory handles
Replace try/finally with 'using' declaration for automatic resource cleanup. This prevents ERR_DIR_CLOSED errors when the async iterator auto-closes the directory handle and then finally block tries to close it again. Key changes: - Wrap Dir in AsyncDisposable object with Symbol.asyncDispose - Use Promise.resolve() to handle Bun's synchronous close() behavior - Properly catch errors if handle is already closed - Tests verify correct cleanup in all scenarios (early break, full iteration) The 'using' pattern ensures cleanup happens at scope exit, even with early returns or exceptions, while gracefully handling double-close.
1 parent 10af068 commit 3d6c7ef

File tree

1 file changed

+17
-11
lines changed

1 file changed

+17
-11
lines changed

src/services/tools/file_list.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,28 @@ export function createFileListTool(config: { cwd: string }) {
166166
return { entries: [], totalCount: currentCount.value, exceeded: true };
167167
}
168168

169-
// Use opendir for iterative reading - more memory efficient and allows early termination
170-
let dirHandle;
171169
const dirents = [];
172170
try {
173-
dirHandle = await fs.opendir(dir);
174-
171+
const dirObj = await fs.opendir(dir);
172+
// Use opendir for iterative reading - more memory efficient and allows early termination
173+
// Wrap in AsyncDisposable for automatic cleanup via 'using'
174+
await using dirHandle = {
175+
dir: dirObj,
176+
async [Symbol.asyncDispose]() {
177+
try {
178+
// close() may return void or Promise depending on runtime
179+
await Promise.resolve(dirObj.close());
180+
} catch {
181+
// Ignore errors if already closed (e.g., by iterator completion)
182+
}
183+
},
184+
};
185+
175186
// Read directory entries iteratively to avoid allocating large arrays
176187
// and to allow early termination if we reach the limit
177-
for await (const dirent of dirHandle) {
188+
for await (const dirent of dirHandle.dir) {
178189
dirents.push(dirent);
179-
190+
180191
// Early termination: stop reading if we've collected enough entries
181192
// (accounts for filtering, so we read a bit more than the limit)
182193
if (dirents.length > options.maxEntries * 2) {
@@ -186,11 +197,6 @@ export function createFileListTool(config: { cwd: string }) {
186197
} catch {
187198
// If we can't read the directory (permissions, etc.), skip it
188199
return { entries: [], totalCount: currentCount.value, exceeded: false };
189-
} finally {
190-
// Always close the directory handle
191-
if (dirHandle) {
192-
await dirHandle.close();
193-
}
194200
}
195201

196202
// Sort: directories first, then files, alphabetically within each group

0 commit comments

Comments
 (0)