Compare commits

...

2 Commits

Author SHA1 Message Date
autofix-ci[bot]
e030a1c144 [autofix.ci] apply automated fixes 2025-11-27 03:41:28 +00:00
Claude
9aa856e16e fix: handle file.stat() errors for empty .env files on WSL1/Docker
On WSL1 and certain Docker configurations, calling file.stat() on empty
.env files could fail with unexpected errors, causing script execution
to fail entirely with no output.

This fix adds fallback logic to use file.getEndPos() when file.stat()
fails, ensuring empty .env files are handled gracefully across all
platforms and filesystems.

**Changes:**
- Add error handling for file.stat() in loadEnvFile()
- Add error handling for file.stat() in loadEnvFileDynamic()
- Fall back to file.getEndPos() when stat() fails
- Add comprehensive tests for empty .env file scenarios

**Testing:**
- All existing .env tests pass
- New tests cover empty files, whitespace-only, and comment-only cases
- Verified on Windows/Cygwin (WSL1/Docker testing requires user confirmation)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 19:38:26 -08:00
2 changed files with 151 additions and 2 deletions

View File

@@ -751,7 +751,24 @@ pub const Loader = struct {
break :brk pos;
}
const stat = try file.stat();
const stat = file.stat() catch |err| {
// On some systems (WSL1, certain Docker configurations), stat() may fail
// for valid empty files. Try to get the file size using getEndPos() as fallback.
const pos = file.getEndPos() catch {
if (!this.quiet) {
Output.prettyErrorln("<r><red>{s}<r> error getting file size for {s}", .{ @errorName(err), base });
}
@field(this, base) = logger.Source.initPathString(base, "");
return;
};
if (pos == 0) {
@field(this, base) = logger.Source.initPathString(base, "");
return;
}
break :brk pos;
};
if (stat.size == 0 or stat.kind != .file) {
@field(this, base) = logger.Source.initPathString(base, "");
@@ -824,7 +841,24 @@ pub const Loader = struct {
break :brk pos;
}
const stat = try file.stat();
const stat = file.stat() catch |err| {
// On some systems (WSL1, certain Docker configurations), stat() may fail
// for valid empty files. Try to get the file size using getEndPos() as fallback.
const pos = file.getEndPos() catch {
if (!this.quiet) {
Output.prettyErrorln("<r><red>{s}<r> error getting file size for {s}", .{ @errorName(err), file_path });
}
try this.custom_files_loaded.put(file_path, logger.Source.initPathString(file_path, ""));
return;
};
if (pos == 0) {
try this.custom_files_loaded.put(file_path, logger.Source.initPathString(file_path, ""));
return;
}
break :brk pos;
};
if (stat.size == 0 or stat.kind != .file) {
try this.custom_files_loaded.put(file_path, logger.Source.initPathString(file_path, ""));

View File

@@ -0,0 +1,115 @@
// Regression tests for empty .env file issues
// https://github.com/oven-sh/bun/issues/[ISSUE_NUMBER]
// On WSL1 and certain Docker configurations, empty .env files could cause
// file.stat() to fail, preventing script execution entirely.
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
describe("empty .env file handling", () => {
test("empty .env file should not prevent script execution", async () => {
using dir = tempDir("empty-env-file", {
".env": "",
"test.js": "console.log('test');",
});
await using proc = Bun.spawn({
cmd: [bunExe(), "test.js"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toBe("");
expect(stdout.trim()).toBe("test");
expect(exitCode).toBe(0);
});
test("empty .env file with other env files", async () => {
using dir = tempDir("empty-env-with-others", {
".env": "",
".env.local": "FOO=bar",
"test.js": "console.log(process.env.FOO);",
});
await using proc = Bun.spawn({
cmd: [bunExe(), "test.js"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toBe("");
expect(stdout.trim()).toBe("bar");
expect(exitCode).toBe(0);
});
test("multiple empty .env files should not prevent script execution", async () => {
using dir = tempDir("multiple-empty-env", {
".env": "",
".env.local": "",
".env.development": "",
"test.js": "console.log('output');",
});
await using proc = Bun.spawn({
cmd: [bunExe(), "test.js"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toBe("");
expect(stdout.trim()).toBe("output");
expect(exitCode).toBe(0);
});
test("empty .env with whitespace only", async () => {
using dir = tempDir("empty-env-whitespace", {
".env": " \n\n \t \n",
"test.js": "console.log('test');",
});
await using proc = Bun.spawn({
cmd: [bunExe(), "test.js"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout.trim()).toBe("test");
expect(exitCode).toBe(0);
});
test("empty .env with only comments", async () => {
using dir = tempDir("empty-env-comments", {
".env": "# This is a comment\n# Another comment\n",
"test.js": "console.log('test');",
});
await using proc = Bun.spawn({
cmd: [bunExe(), "test.js"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout.trim()).toBe("test");
expect(exitCode).toBe(0);
});
});