Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
46e7757fa2 fix(env): handle fstat errors gracefully when loading .env files
On WSL1, fstat() on certain files (including empty .env files) can fail
unexpectedly. Previously, these errors would propagate up via `try` and
cause the entire script to fail silently with exit code 1.

This change catches fstat() errors in both loadEnvFile() and
loadEnvFileDynamic() and handles them gracefully by:
1. Printing a warning (unless quiet mode is enabled)
2. Marking the .env file as "loaded" (empty) to prevent retries
3. Continuing execution normally

Reproduction steps on WSL1:
1. Create test.js with content: console.log('test');
2. Run: bun test.js (works fine, outputs "test")
3. Create empty .env: touch .env
4. Run: bun test.js (previously: no output, exit 1; now: outputs "test")

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 03:44:13 +00:00
2 changed files with 46 additions and 2 deletions

View File

@@ -751,7 +751,14 @@ pub const Loader = struct {
break :brk pos;
}
const stat = try file.stat();
const stat = file.stat() catch |err| {
// On WSL1, fstat() can fail on certain files. Handle gracefully.
if (!this.quiet) {
Output.prettyErrorln("<r><yellow>warn<r>: {s} when loading {s} file", .{ @errorName(err), base });
}
@field(this, base) = logger.Source.initPathString(base, "");
return;
};
if (stat.size == 0 or stat.kind != .file) {
@field(this, base) = logger.Source.initPathString(base, "");
@@ -824,7 +831,14 @@ pub const Loader = struct {
break :brk pos;
}
const stat = try file.stat();
const stat = file.stat() catch |err| {
// On WSL1, fstat() can fail on certain files. Handle gracefully.
if (!this.quiet) {
Output.prettyErrorln("<r><yellow>warn<r>: {s} when loading {s} file", .{ @errorName(err), file_path });
}
try this.custom_files_loaded.put(file_path, logger.Source.initPathString(file_path, ""));
return;
};
if (stat.size == 0 or stat.kind != .file) {
try this.custom_files_loaded.put(file_path, logger.Source.initPathString(file_path, ""));

View File

@@ -820,6 +820,36 @@ test("NODE_ENV=test loads .env.test even when .env.production exists", () => {
expect(stdout).toBe("test");
});
test("empty .env file does not cause errors", () => {
// Regression test: On WSL1, an empty .env file could cause fstat() to fail,
// which would propagate as an unhandled error and cause the script to fail silently.
const dir = tempDirWithFiles("dotenv-empty", {
".env": "", // Empty .env file
"index.ts": "console.log('success');",
});
const { stdout } = bunRun(`${dir}/index.ts`);
expect(stdout).toBe("success");
});
test("empty .env.local file does not cause errors", () => {
const dir = tempDirWithFiles("dotenv-empty-local", {
".env.local": "", // Empty .env.local file
"index.ts": "console.log('success');",
});
const { stdout } = bunRun(`${dir}/index.ts`);
expect(stdout).toBe("success");
});
test("empty .env alongside non-empty .env.local works", () => {
const dir = tempDirWithFiles("dotenv-empty-mixed", {
".env": "", // Empty
".env.local": "FOO=bar", // Non-empty
"index.ts": "console.log(process.env.FOO);",
});
const { stdout } = bunRun(`${dir}/index.ts`);
expect(stdout).toBe("bar");
});
describe("env loader buffer handling", () => {
test("handles large quoted values with escape sequences", () => {
// This test ensures the env loader properly handles large values that exceed the initial buffer size