fix(watch): replace panic with helpful error on fd exhaustion

When the file watcher fails to initialize due to exhausted file
descriptors (ProcessFdQuotaExceeded), route through handleRootError
instead of Output.panic. This provides actionable remediation steps
(ulimit, sysctl, limits.conf) and exits cleanly instead of showing
the misleading "oh no: Bun has crashed" message.

Closes #26914

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2026-02-11 21:52:48 +00:00
parent 50e478dcdc
commit 5d0322536f

View File

@@ -87,12 +87,10 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
clear_screen = clear_screen_flag;
const watcher = Watcher.init(Reloader, reloader, fs, bun.default_allocator) catch |err| {
bun.handleErrorReturnTrace(err, @errorReturnTrace());
Output.panic("Failed to enable File Watcher: {s}", .{@errorName(err)});
bun.crash_handler.handleRootError(err, @errorReturnTrace());
};
watcher.start() catch |err| {
bun.handleErrorReturnTrace(err, @errorReturnTrace());
Output.panic("Failed to start File Watcher: {s}", .{@errorName(err)});
bun.crash_handler.handleRootError(err, @errorReturnTrace());
};
return watcher;
}
@@ -259,8 +257,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
this.transpiler.fs,
bun.default_allocator,
) catch |err| {
bun.handleErrorReturnTrace(err, @errorReturnTrace());
Output.panic("Failed to enable File Watcher: {s}", .{@errorName(err)});
bun.crash_handler.handleRootError(err, @errorReturnTrace());
} }
else
.{ .hot = Watcher.init(
@@ -269,8 +266,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
this.transpiler.fs,
bun.default_allocator,
) catch |err| {
bun.handleErrorReturnTrace(err, @errorReturnTrace());
Output.panic("Failed to enable File Watcher: {s}", .{@errorName(err)});
bun.crash_handler.handleRootError(err, @errorReturnTrace());
} };
if (reload_immediately) {
@@ -285,15 +281,16 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
this.transpiler.fs,
bun.default_allocator,
) catch |err| {
bun.handleErrorReturnTrace(err, @errorReturnTrace());
Output.panic("Failed to enable File Watcher: {s}", .{@errorName(err)});
bun.crash_handler.handleRootError(err, @errorReturnTrace());
};
this.transpiler.resolver.watcher = bun.resolver.ResolveWatcher(*Watcher, Watcher.onMaybeWatchDirectory).init(this.bun_watcher.?);
}
clear_screen = !this.transpiler.env.hasSetNoClearTerminalOnReload(!Output.enable_ansi_colors_stdout);
reloader.getContext().start() catch @panic("Failed to start File Watcher");
reloader.getContext().start() catch |err| {
bun.crash_handler.handleRootError(err, @errorReturnTrace());
};
}
fn putTombstone(this: *@This(), key: []const u8, value: *bun.fs.FileSystem.RealFS.EntriesOption) void {