Bun V1.3.2: Empty Binary Issue In Nix Sandbox
Introduction
This article delves into a peculiar issue encountered with Bun v1.3.2, where the Bun.build() function, when used with the compile option within a Nix sandbox environment, generates empty (0-byte) binaries. This unexpected behavior deviates from the expected outcome of producing a functional executable, and this article aims to explore the steps to reproduce the issue, analyze the potential causes, and discuss attempted workarounds. Understanding this issue is crucial for developers relying on Bun for building and compiling applications within Nix environments.
Reproducing the Bug
To effectively address the problem, it's essential to understand how to replicate it. The bug manifests specifically when building projects using Bun v1.3.2 within a Nix sandbox. Below are detailed steps to reproduce this issue, providing a clear path for developers to confirm the bug on their systems.
Building opencode in Nix
-
Execute the command
nix build github:delafthi/nixpkgs/bun-v1.3.2-build-issues#opencode. This command instructs Nix to build theopencodeproject from a specific branch in a GitHub repository, which is set up to demonstrate the issue. -
After the build process completes, investigate the output located in the
resultdirectory. Specifically, examine the binary fileresult/opencode-darwin-arm64/bin/opencodeusing thestatcommand.> stat result/opencode-darwin-arm64/bin/opencode File: result/opencode-darwin-arm64/bin/opencode size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 1000013h/16777235d Inode: 212640490 Links: 1 Access: (0555/-r-xr-xr-x) Uid: ( 0/ root) Gid: ( 0/ wheel) Access: 2025-11-12 16:49:04.000000000 +0100 Modify: 1970-01-01 01:00:01.000000000 +0100 Change: 2025-11-12 16:49:04.774143637 +0100 Birth: 1970-01-01 01:00:01.000000000 +0100The output from the
statcommand will reveal that theopencodebinary is an empty file with a size of 0 bytes, indicating the bug has been reproduced.
Building opencode Manually in Nix Dev Shell
For a more controlled environment and deeper insight into the build process, you can manually build the project within a Nix development shell. This method mirrors the automated build steps but allows for inspection at each stage.
-
First, build the
node_modulesdependency using the commandnix build github:delafthi/nixpkgs/bun-v1.3.2-build-issues#opencode.node_modules. This ensures that all necessary Node.js modules are available. -
Enter the Nix development shell by running
nix develop github:delafthi/nixpkgs/bun-v1.3.2-build-issues#opencode. This command sets up an isolated environment with the required dependencies and tools. -
Execute the
unpackPhasecommand to unpack the source code of the project. -
Enter the source directory by running the
sourcecommand. -
Apply any necessary patches using the
patchPhasecommand. -
Copy the previously built
node_modulesinto the source directory usingcp -R ../result/. .. This step makes the dependencies available within the development shell. -
Run the prebuild tasks by executing the
buildPhasecommand. Note that a warning about the absence of a Makefile is expected and can be disregarded for this issue. -
Navigate to the
packages/opencodedirectory. -
Execute the Bun build script using the command
bun run ./script/build.ts --single. This command triggers the build process that utilizesBun.build()with thecompileoption. -
Inspect the output in the
distdirectory. Use thestatcommand on the generated binary file, typically located atdist/opencode-darwin-arm64/bin/opencode.[bsh] > stat dist/opencode-darwin-arm64/bin/opencode File: dist/opencode-darwin-arm64/bin/opencode Size: 84903824 Blocks: 165832 IO Block: 4096 regular file Device: 1,14 Inode: 116763021 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 501/delafthi) Gid: ( 20/ staff) Access: 2025-11-12 16:59:48.452880996 +0100 Modify: 2025-11-12 16:59:48.552529779 +0100 Change: 2025-11-12 16:59:48.557518172 +0100 Birth: 2025-11-12 16:59:48.401702226 +0100In this manual build scenario, the
statcommand should reveal a functional binary with a non-zero size, contrasting with the empty binary produced in the automated Nix build.
By following these steps, developers can reliably reproduce the bug and gain a clearer understanding of the context in which it occurs. The manual build process, in particular, offers a valuable point of comparison, highlighting the discrepancy between sandboxed and non-sandboxed builds.
Expected Behavior
The expected behavior when building a project with Bun is to produce a functional binary that can be executed. This binary should contain the compiled code and any necessary runtime components, allowing the application to run as intended. In the case of the opencode project, the expectation is to have an executable binary of a substantial size, reflecting the compiled code and dependencies. This expected outcome was indeed the case in previous versions of Bun, such as v1.3.1.
To illustrate the expected behavior, building the same project with Bun v1.3.1 yields a functional binary. This can be demonstrated using the following steps:
-
Run
nix build github:delafthi/nixpkgs/bun-v1.3.1-no-build-issues. This command builds theopencodeproject using Bun v1.3.1 within a Nix environment. -
After the build completes, inspect the output binary using the
statcommand:> stat result/opencode-darwin-arm64/bin/opencode File: result/opencode-darwin-arm64/bin/opencode size: 84755072 Blocks: 165544 IO Block: 4096 regular file Device: 1000013h/16777235d Inode: 212946100 Links: 1 Access: (0555/-r-xr-xr-x) Uid: ( 0/ root) Gid: ( 0/ wheel) Access: 2025-11-12 17:10:22.000000000 +0100 Modify: 1970-01-01 01:00:01.000000000 +0100 Change: 2025-11-12 17:10:23.011511558 +0100 Birth: 1970-01-01 01:00:01.000000000 +0100
The output from the stat command shows that the binary file has a size of approximately 84MB, which is indicative of a functional executable. This contrasts sharply with the 0-byte binary produced by Bun v1.3.2 in the Nix sandbox, highlighting the regression introduced in the newer version.
The discrepancy between the expected behavior (a functional binary) and the observed behavior (an empty binary) underscores the significance of this issue. Developers rely on build tools to produce working executables, and the failure to do so can disrupt development workflows and deployment processes. Therefore, understanding and resolving this bug is crucial for maintaining the integrity and reliability of Bun as a build tool.
Observed Behavior
Instead of the expected functional binary, Bun v1.3.2, when used with the Bun.build() API and the compile option in a Nix sandbox, produces an empty binary. This means that the output file is a 0-byte file, rendering it unusable as an executable. This behavior is a significant deviation from the norm, as build tools are designed to generate working executables. The observed outcome directly contradicts the purpose of the build process, leading to broken builds and hindering development workflows.
The reproduction steps outlined earlier clearly demonstrate this issue. When the opencode project is built using Nix with Bun v1.3.2, the resulting binary file is consistently empty. This can be verified by examining the file size using the stat command, as shown in the reproduction section.
The implications of this observed behavior are substantial. An empty binary is, in essence, a failed build artifact. It cannot be executed, deployed, or used in any meaningful way. This issue can lead to significant delays in development cycles, as developers must identify and work around the problem before proceeding. Furthermore, it erodes confidence in the build tool, as the fundamental guarantee of producing a working executable is broken.
This observed behavior raises questions about the interaction between Bun v1.3.2, the Bun.build() API, the compile option, and the Nix sandbox environment. Understanding the root cause of this issue is critical for developing effective solutions and preventing future occurrences. The subsequent sections of this article will delve into potential causes and attempted workarounds, aiming to shed light on the underlying mechanisms at play.
Additional Information and Suspected Cause
Further investigation reveals that the issue seems specific to using the Bun.build() API with the compile option within sandboxed environments like Nix. Building the package works correctly in Bun v1.3.1 and when using the bun run ./script/build.ts --single CLI command directly. This suggests that the bug is not a general problem with Bun's build process but rather a nuanced interaction between the API, the compilation process, and the sandboxed environment.
The suspicion is that the sandboxed environment, with its restricted access to system resources and file system, might be interfering with the compilation process triggered by Bun.build(). Sandboxes are designed to isolate build processes, ensuring reproducibility and preventing unintended side effects. However, this isolation can sometimes lead to unexpected behavior if the build tool relies on certain system-level operations that are restricted within the sandbox.
Attempted Workarounds
Several workarounds were attempted to mitigate the issue, but none proved effective. These attempts highlight the complexity of the problem and the challenges in resolving it without a deeper understanding of the root cause. The workarounds included:
- Using
writableTmpDirAsHomeHook: This approach aims to provide a writable home directory within the Nix sandbox. Some build processes rely on writing temporary files to the home directory, and if this is restricted, it can lead to build failures. However, this workaround did not resolve the empty binary issue, suggesting that the problem is not directly related to home directory access. - Setting
TMPDIR=$(mktemp -d): This workaround explicitly sets theTMPDIRenvironment variable to a newly created temporary directory. This is intended to ensure that temporary file operations have a dedicated and writable location. Despite this, the issue persisted, indicating that the problem is not simply a matter of temporary directory access. - Adding
await Bun.sleep(5000)afterBun.build(): This attempt was based on the hypothesis that the issue might be due to asynchronous I/O operations not completing before the build process terminates. Adding a 5-second delay after theBun.build()call was meant to give the process time to finish writing the output file. However, this workaround also failed, suggesting that the problem is not related to asynchronous operations being cut short.
The failure of these workarounds underscores the need for a more thorough investigation into the interaction between Bun v1.3.2, the Bun.build() API, the compile option, and the Nix sandbox environment. A deeper understanding of the underlying mechanisms is essential for developing an effective solution.
Conclusion
The issue of Bun v1.3.2 producing empty binaries within a Nix sandbox environment when using Bun.build() with the compile option is a significant problem that warrants attention. The reproduction steps clearly demonstrate the bug, and the attempted workarounds highlight the complexity of the issue. While the exact root cause remains elusive, the evidence suggests a nuanced interaction between Bun's build process and the restrictions imposed by the sandbox. Further investigation and debugging are necessary to identify the underlying mechanism and develop a robust solution.
It is recommended that developers encountering this issue consider using Bun v1.3.1 as a temporary workaround, as this version does not exhibit the same behavior. Additionally, monitoring the Bun issue tracker and community forums for updates on this bug is advisable. Collaborating with the Bun development team and sharing insights can contribute to a faster resolution.
For more information on Nix sandboxes and their behavior, you can refer to the official Nix documentation on the Nix website.