← Back to PRs

#14023: fix: filter skills watcher to relevant file types to prevent FD exhaustion

by funmerlin open 2026-02-11 10:07 View on GitHub →
agents size: S
## Problem The skills file watcher (`chokidar`) in `src/agents/skills/refresh.ts` watches the entire skills directory tree with `awaitWriteFinish` polling enabled. When a skill contains a large number of asset files (e.g. an SVG icon pack with 37,000+ files), chokidar opens one file descriptor per watched file for write-finish polling. This causes **FD exhaustion on macOS**, eventually preventing the gateway from spawning child processes (`EBADF`). ### Observed behavior - Gateway process accumulated **37,439 open file descriptors** overnight - All FDs were REG (regular file) handles to SVG files in a single skill directory - `exec` tool calls failed with `spawn EBADF` - Gateway restart did not help — the FDs were opened immediately at startup ### Root cause `ensureSkillsWatcher()` creates a chokidar watcher on skill directories with: ```js awaitWriteFinish: { stabilityThreshold: debounceMs, pollInterval: 100, } ``` The `awaitWriteFinish` option causes chokidar to poll files for stability, which requires opening a file descriptor for each watched file. The existing `ignored` patterns only excluded `.git`, `node_modules`, `dist`, and Python cache directories — not asset files. ## Fix Add an allowlist of skill-relevant file extensions (`.md`, `.js`, `.ts`, `.json`, `.yaml`, `.py`, `.sh`, etc.) to the watcher's ignored patterns. Non-relevant files (images, SVGs, audio, video, fonts, archives, etc.) are now excluded from the watcher. This keeps FD usage proportional to actual skill code rather than total asset count. ### Before ``` $ lsof -p $(pgrep -f openclaw-gateway) | wc -l 37439 ``` ### After ``` $ lsof -p $(pgrep -f openclaw-gateway) | wc -l 119 ``` ## Changes - `src/agents/skills/refresh.ts`: Added `SKILLS_WATCH_RELEVANT_EXTENSIONS` allowlist and a filter function to `DEFAULT_SKILLS_WATCH_IGNORED` - `src/agents/skills/refresh.test.ts`: Updated tests to handle mixed RegExp/function ignored array, added test for asset file filtering <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR updates the skills file watcher (`src/agents/skills/refresh.ts`) to avoid file-descriptor exhaustion by ignoring non-skill asset files and only watching skill-relevant file types. Tests in `src/agents/skills/refresh.test.ts` were updated to accommodate the new `ignored` rules (now supporting both RegExp and function predicates) and to assert that common asset extensions are ignored. The change fits into the skills snapshot pipeline by reducing watcher churn/FD usage while preserving the existing behavior of bumping the skills snapshot version on relevant filesystem events, which is consumed by session snapshot refresh logic. <h3>Confidence Score: 4/5</h3> - This PR is likely safe to merge once the remaining placeholder reference is resolved. - The watcher change is narrowly scoped and tests were updated accordingly. The only definite merge-blocker spotted in the diff is a placeholder issue URL left in a source comment, which should be replaced or removed to avoid shipping misleading references. - src/agents/skills/refresh.ts <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs