mirror of
https://github.com/oven-sh/bun
synced 2026-02-11 19:38:58 +00:00
Fix shell cat panic on Windows when reader chunks arrive late
Fixes a race condition where IO reader chunks could arrive after the cat builtin transitioned to terminal states (.done, .waiting_write_err, .idle), causing a panic with "Invalid state". This was particularly reproducible on Windows in package scripts. The fix handles these terminal states gracefully in onIOReaderChunk() by ignoring late-arriving chunks since the reader is already being cleaned up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -196,9 +196,10 @@ pub fn onIOReaderChunk(this: *Cat, chunk: []const u8, remove: *bool) Yield {
|
||||
_ = this.bltn().writeNoIO(.stdout, chunk);
|
||||
return .done;
|
||||
},
|
||||
else => @panic("Invalid state"),
|
||||
// Race condition: chunks can arrive after state transitions (especially on Windows)
|
||||
// Just ignore them - the reader is being cleaned up
|
||||
.done, .waiting_write_err, .idle => return .done,
|
||||
}
|
||||
return .done;
|
||||
}
|
||||
|
||||
pub fn onIOReaderDone(this: *Cat, err: ?jsc.SystemError) Yield {
|
||||
|
||||
54
test/regression/issue/23370.test.ts
Normal file
54
test/regression/issue/23370.test.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
|
||||
|
||||
test("cat should not panic on delayed reader chunks (issue #23370)", async () => {
|
||||
const dir = tempDirWithFiles("cat-race", {
|
||||
"file1.txt": "a".repeat(1024 * 1024), // 1MB
|
||||
"file2.txt": "b".repeat(1024 * 1024), // 1MB
|
||||
"file3.txt": "c".repeat(1024 * 1024), // 1MB
|
||||
"test.js": `
|
||||
const { $ } = require("bun");
|
||||
await $\`cat file1.txt file2.txt file3.txt\`;
|
||||
`,
|
||||
});
|
||||
|
||||
const proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test.js"],
|
||||
cwd: dir,
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [exitCode, stderr] = await Promise.all([proc.exited, proc.stderr.text()]);
|
||||
|
||||
expect(stderr).not.toContain("panic");
|
||||
expect(stderr).not.toContain("Invalid state");
|
||||
expect(exitCode).toBe(0);
|
||||
}, 10000);
|
||||
|
||||
test("cat in package script should not panic", async () => {
|
||||
const dir = tempDirWithFiles("cat-pkgscript", {
|
||||
"file.txt": "x".repeat(1024 * 1024), // 1MB
|
||||
"package.json": JSON.stringify({
|
||||
name: "test",
|
||||
scripts: {
|
||||
test: "cat file.txt",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const proc = Bun.spawn({
|
||||
cmd: [bunExe(), "run", "test"],
|
||||
cwd: dir,
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const [exitCode, stderr] = await Promise.all([proc.exited, proc.stderr.text()]);
|
||||
|
||||
expect(stderr).not.toContain("panic");
|
||||
expect(stderr).not.toContain("Invalid state");
|
||||
expect(exitCode).toBe(0);
|
||||
}, 10000);
|
||||
Reference in New Issue
Block a user