Skip to content

fix: prevent file descriptor exhaustion when watching for EF changes - #8387

Merged
paulo merged 2 commits into
mainfrom
pa/fix-fd
Aug 4, 2026
Merged

fix: prevent file descriptor exhaustion when watching for EF changes#8387
paulo merged 2 commits into
mainfrom
pa/fix-fd

Conversation

@paulo

@paulo paulo commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes an issue where netlify dev would fail to load or reload Edge Functions with spawn EBADF in large projects, particularly on macOS with Node 24+ (RUN-2836).

To support live reloading of Edge Functions, the CLI watched the entire project directory for file changes. On macOS, the file watcher consumes one file descriptor per watched file, so in projects with many thousands of files this pushed the process to tens of thousands of open descriptors. Once past that point, spawning any child process on macOS fails with EBADF - regardless of the configured file descriptor limit - which meant the Deno process that runs Edge Functions locally could not be started or restarted. The result was a dev server that either failed to load Edge Functions at startup or crashed when reloading them after a file change.

The fix: Instead of watching the whole project, the dev server now only watches the directories where Edge Functions live, plus the specific local files that functions import from elsewhere in the project. That import list is refreshed after every rebuild, so live reloading keeps working for functions and their dependencies - including files outside the functions directories - while the number of watched files (and open file descriptors) stays small and independent of project size.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved edge-function file watching for faster, more targeted updates.
    • External dependencies are automatically monitored when needed and removed when no longer relevant.
    • Added support for retrieving all keys from a multimap.
  • Tests

    • Added coverage for edge-function directory watching and dependency tracking.
    • Updated watcher tests for the improved setup and ignore behavior.

Walkthrough

The edge-function registry now watches targeted edge-function directories instead of the project root. It tracks external dependencies from the processed module graph and updates the watcher as dependencies change. MultiMap now exposes a keys() iterator. Unit tests cover directory selection, serve-mode framework paths, and dependency watcher updates.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: serhalp, eduardoboucas

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: preventing file descriptor exhaustion when watching Edge Function changes.
Description check ✅ Passed The description accurately explains the EBADF failure, its cause, and the targeted watcher fix.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pa/fix-fd

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

📊 Benchmark results

Comparing with d4945e2

  • Dependency count: 1,165 (no change)
  • Package size: 455 MB (no change)
  • Number of ts-expect-error directives: 358 (no change)

@paulo
paulo requested review from eduardoboucas and serhalp August 3, 2026 09:56
@paulo
paulo marked this pull request as ready for review August 3, 2026 10:36
@paulo
paulo requested a review from a team as a code owner August 3, 2026 10:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/edge-functions/registry.ts (1)

713-737: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Resync the root directory watcher when edgeFunctionsDirectories changes.

setupFunctionsWatcher() is only called from setupWatchers(), so functionsWatcher keeps the initial edge-function directories. The config-file watcher updates declarations and detects added/deleted functions, but it never changes functionsWatcher’s watched paths. If build.edge_functions targets a new directory during netlify dev, files in that directory are not watched, so live reload stops working for it until the CLI restarts. Recompute and recreate functionsWatcher when edgeFunctionsDirectories changes after a config reload.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/edge-functions/registry.ts` around lines 713 - 737, Update the config
reload callback in setupWatchers so it compares the reloaded
edgeFunctionsDirectories with the currently watched directories and recreates
functionsWatcher when they differ. Reuse setupFunctionsWatcher (and clean up or
replace the existing watcher as appropriate) after updating declarations, while
preserving the existing checkForAddedOrDeletedFunctions flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/lib/edge-functions/registry.ts`:
- Around line 713-737: Update the config reload callback in setupWatchers so it
compares the reloaded edgeFunctionsDirectories with the currently watched
directories and recreates functionsWatcher when they differ. Reuse
setupFunctionsWatcher (and clean up or replace the existing watcher as
appropriate) after updating declarations, while preserving the existing
checkForAddedOrDeletedFunctions flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 19b3f024-c1b6-48c2-94ac-05c41fe6a733

📥 Commits

Reviewing files that changed from the base of the PR and between 6492300 and ff4fff6.

📒 Files selected for processing (4)
  • src/lib/edge-functions/registry.ts
  • src/utils/multimap.ts
  • tests/unit/lib/edge-functions/watch-ignore.test.ts
  • tests/unit/lib/edge-functions/watchers.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • netlify/blueprints (manual)

@paulo
paulo enabled auto-merge (squash) August 4, 2026 09:40
@paulo
paulo merged commit 15f20fa into main Aug 4, 2026
36 checks passed
@paulo
paulo deleted the pa/fix-fd branch August 4, 2026 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants