Skip to content

fix: Pipe process.stdout.write through Buffer to fix node incompatibility#29232

Open
lindskogen wants to merge 1 commit intooven-sh:mainfrom
lindskogen:bug/stdout-write-ignores-encoding
Open

fix: Pipe process.stdout.write through Buffer to fix node incompatibility#29232
lindskogen wants to merge 1 commit intooven-sh:mainfrom
lindskogen:bug/stdout-write-ignores-encoding

Conversation

@lindskogen
Copy link
Copy Markdown

What does this PR do?

Happened upon a bug in bun, where it currently ignores the encoding parameter of process.stdout.write. This PR makes it so it uses Buffer.from to encode the data.

How did you verify your code works?

  • I used claude code to generate a few test cases and built bun locally to verify compatibility with node.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

Walkthrough

The changes add input normalization for string encoding handling in file stream write operations and introduce comprehensive regression tests for stdout write operations with various encodings.

Changes

Cohort / File(s) Summary
File Stream Write Normalization
src/js/internal/fs/streams.ts
Added buffer conversion step in three write paths (_write, underscoreWriteFast, writeFast) to normalize string data with non-UTF-8 encodings before passing to fileSink.write.
Process Stdout Write Tests
test/js/node/process/process-stdio.test.ts
Added regression tests for process.stdout.write() validating encoding behavior with "binary", "latin1", "hex", and default UTF-8 encodings, ensuring correct byte sequences are emitted.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: addressing a Node.js incompatibility in process.stdout.write by piping through Buffer to handle encoding parameters.
Description check ✅ Passed The pull request description follows the required template with both sections completed. It clearly explains what the PR does and how it was verified.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/js/node/process/process-stdio.test.ts`:
- Around line 164-204: Each test that spawns a process (the three tests using
spawnSync with bunExe and bunEnv) lacks an assertion on the child process exit
code; update each test to capture the spawnSync result's exitCode (from the
object returned by spawnSync) and add an assertion after the existing stdout
Buffer checks to ensure exitCode is 0 (or the expected success code). Locate the
three tests named "process.stdout.write(string, '%s') writes raw bytes",
"process.stdout.write(string, 'hex') decodes hex", and
"process.stdout.write(string) defaults to UTF-8" and add the exitCode assertion
immediately after the Buffer.compare / stdout checks for each spawnSync call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4401ef1f-f0f0-40a2-9359-e7472a07009e

📥 Commits

Reviewing files that changed from the base of the PR and between 7c75a87 and 0af9fb9.

📒 Files selected for processing (2)
  • src/js/internal/fs/streams.ts
  • test/js/node/process/process-stdio.test.ts

Comment on lines +164 to +204
test.each(["binary", "latin1"] as const)("process.stdout.write(string, '%s') writes raw bytes", encoding => {
const { stdout } = spawnSync({
cmd: [
bunExe(),
"-e",
`for (let i = 0; i <= 0xff; i++) process.stdout.write(String.fromCharCode(i), ${JSON.stringify(encoding)});`,
],
stdout: "pipe",
stdin: null,
stderr: "inherit",
env: bunEnv,
});
expect(stdout).toBeInstanceOf(Buffer);
expect(stdout.length).toBe(256);
const expected = Buffer.alloc(256);
for (let i = 0; i < 256; i++) expected[i] = i;
expect(Buffer.compare(stdout, expected)).toBe(0);
});

test("process.stdout.write(string, 'hex') decodes hex", () => {
const { stdout } = spawnSync({
cmd: [bunExe(), "-e", `process.stdout.write("deadbeef", "hex");`],
stdout: "pipe",
stdin: null,
stderr: "inherit",
env: bunEnv,
});
expect(Buffer.compare(stdout, Buffer.from([0xde, 0xad, 0xbe, 0xef]))).toBe(0);
});

test("process.stdout.write(string) defaults to UTF-8", () => {
const { stdout } = spawnSync({
cmd: [bunExe(), "-e", `process.stdout.write("héllo");`],
stdout: "pipe",
stdin: null,
stderr: "inherit",
env: bunEnv,
});
// é = U+00E9 = UTF-8 c3 a9
expect(Buffer.compare(stdout, Buffer.from([0x68, 0xc3, 0xa9, 0x6c, 0x6c, 0x6f]))).toBe(0);
});
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.

⚠️ Potential issue | 🟠 Major

Add exit-code assertions to each new spawnSync regression test.

Each new test validates stdout bytes but never asserts process success. Please capture exitCode and assert it after stdout checks in all three tests.

Suggested patch
-    const { stdout } = spawnSync({
+    const { stdout, exitCode } = spawnSync({
       cmd: [
         bunExe(),
         "-e",
         `for (let i = 0; i <= 0xff; i++) process.stdout.write(String.fromCharCode(i), ${JSON.stringify(encoding)});`,
       ],
@@
     expect(stdout.length).toBe(256);
     const expected = Buffer.alloc(256);
     for (let i = 0; i < 256; i++) expected[i] = i;
     expect(Buffer.compare(stdout, expected)).toBe(0);
+    expect(exitCode).toBe(0);
   });

   test("process.stdout.write(string, 'hex') decodes hex", () => {
-    const { stdout } = spawnSync({
+    const { stdout, exitCode } = spawnSync({
       cmd: [bunExe(), "-e", `process.stdout.write("deadbeef", "hex");`],
       stdout: "pipe",
       stdin: null,
       stderr: "inherit",
       env: bunEnv,
     });
     expect(Buffer.compare(stdout, Buffer.from([0xde, 0xad, 0xbe, 0xef]))).toBe(0);
+    expect(exitCode).toBe(0);
   });

   test("process.stdout.write(string) defaults to UTF-8", () => {
-    const { stdout } = spawnSync({
+    const { stdout, exitCode } = spawnSync({
       cmd: [bunExe(), "-e", `process.stdout.write("héllo");`],
       stdout: "pipe",
       stdin: null,
       stderr: "inherit",
       env: bunEnv,
     });
     // é = U+00E9 = UTF-8 c3 a9
     expect(Buffer.compare(stdout, Buffer.from([0x68, 0xc3, 0xa9, 0x6c, 0x6c, 0x6f]))).toBe(0);
+    expect(exitCode).toBe(0);
   });

As per coding guidelines, “Always check exit codes and test error scenarios when spawning processes in tests” and “Expect stdout assertions before exit code assertions.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/js/node/process/process-stdio.test.ts` around lines 164 - 204, Each test
that spawns a process (the three tests using spawnSync with bunExe and bunEnv)
lacks an assertion on the child process exit code; update each test to capture
the spawnSync result's exitCode (from the object returned by spawnSync) and add
an assertion after the existing stdout Buffer checks to ensure exitCode is 0 (or
the expected success code). Locate the three tests named
"process.stdout.write(string, '%s') writes raw bytes",
"process.stdout.write(string, 'hex') decodes hex", and
"process.stdout.write(string) defaults to UTF-8" and add the exitCode assertion
immediately after the Buffer.compare / stdout checks for each spawnSync call.

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.

1 participant