What this usually means
Bun implements most Node.js APIs but not all. The most common culprits are missing Node.js built-in modules (like 'node:buffer' before Bun 1.0.3), differences in module resolution (ESM vs CJS interop), missing globals (e.g., 'process.nextTick' moved to a different binding), and unsupported native addons that rely on Node.js internal bindings. Bun also has its own built-in APIs (e.g., 'Bun.file') that conflict with Node.js equivalents. Often the error message is vague—like 'TypeError: ... is not a function'—because Bun's JIT fails to polyfill a Node.js API that exists in the global scope of Node.
The first ten minutes — establish facts before touching code.
- 1Check Bun version: run 'bun --version' and compare to the latest. Many compat fixes land in point releases.
- 2Run your app with 'bun --bun' to enable Node.js compatibility shims (this flag changes module resolution).
- 3Set environment variable 'NODE_ENV=production' and re-run to eliminate dev-only polyfills.
- 4Search the error message against Bun's issue tracker: 'bun issue <error-message>' on GitHub.
- 5Isolate the failing module by commenting out imports until the error disappears.
- 6Run 'bun build --target=node' on your entry point to see if Bun's bundler catches incompatibilities.
The specific files, logs, configs, and dashboards that usually own this bug.
- searchbun.lockb or bun.lock (lockfile) for version mismatches of Node.js polyfill packages
- searchnode_modules/.bin/ and the actual module resolution path—Bun might resolve to a different version than Node
- searchbunfig.toml in project root—settings like 'runtime' or 'nodejs_modules' affect compat
- searchThe failing module's package.json: check 'exports' field and 'type' (CJS vs ESM)
- searchBun's GitHub issue tracker for known compat issues with specific packages (e.g., 'bcrypt')
- searchApplication logs with 'BUN_DEBUG=1' prefix to get verbose runtime diagnostics
- searchThe import map or alias in tsconfig.json or jsconfig.json—Bun handles these differently
Practical causes, not theory. These are the things you will actually find.
- warningUsing 'node:buffer' import before Bun v1.0.3 (Bun now supports it, but older versions don't)
- warningCommonJS module tries to access 'process.nextTick' which Bun exposes differently (use 'queueMicrotask' instead)
- warningNative addon (e.g., 'sharp', 'bcrypt') compiled against Node.js ABI, not Bun's JavaScriptCore
- warningMissing 'globalThis.Buffer' in environments where Bun's global shims aren't loaded
- warningESM/CJS interop: Bun's module loader doesn't handle 'require()' inside ESM as Node does
- warningThird-party package dynamically requires 'node:fs' which Bun treats differently (case sensitivity on macOS vs Linux)
Concrete fix directions. Pick the one that matches your root cause.
- buildReplace 'node:buffer' with 'buffer' (the npm polyfill) or upgrade Bun to >=1.0.3
- buildUse 'queueMicrotask' instead of 'process.nextTick', or add a polyfill at entry: 'globalThis.process = { nextTick: queueMicrotask }'
- buildFor native addons, use Bun's built-in alternatives: 'Bun.password.hash()' replaces 'bcrypt', 'Bun.file' replaces 'fs.readFileSync'
- buildForce ESM mode: set 'type: module' in package.json and use import/export consistently
- buildAdd a 'bunfig.toml' with '[runtime]' section to enable Node.js compat flags
- buildRun with '--compat' flag (if using older Bun) to enable broader Node.js compatibility layer
A fix you cannot prove is a guess. Close the loop.
- verifiedRun your test suite with both Node.js and Bun: 'node --experimental-vm-modules node_modules/.bin/jest && bun test'
- verifiedCheck that the error no longer appears when using the fixed import path or polyfill
- verifiedVerify that the fix works in a clean environment: delete node_modules and lockfile, reinstall with 'bun install'
- verifiedMonitor process memory and CPU usage to ensure no performance regression from polyfills
- verifiedAdd a CI step that runs your app with Bun on a minimal test case to catch regressions early
- verifiedUse 'bun run --smol' to test in a constrained memory environment—some compat issues only surface under pressure
Things that make this bug worse or harder to find.
- warningBlindly adding 'process.nextTick' polyfills without checking if Bun already provides it under a different name
- warningAssuming Bun supports all Node.js built-in modules exactly as per Node.js LTS—always check Bun's compat table
- warningUsing 'npm install' instead of 'bun install'—Bun's package manager can resolve dependencies differently
- warningIgnoring the exit code: Bun might exit 0 even if it failed to load a polyfill (check stderr explicitly)
- warningPatching node_modules directly—the changes will be overwritten on next install
- warningRelying on '--bun' flag as a magic bullet—it only helps with module resolution, not missing APIs
Bun Compatibility Error with Bcrypt in Production
Timeline
- 09:15Deploy microservice to ECS using Bun 1.0.1. Health check passes.
- 09:18First user login fails with 500: 'TypeError: process.nextTick is not a function'
- 09:22Check logs—error originates from node_modules/bcrypt/index.js line 42
- 09:30Local Node.js works fine, but Bun fails. Search Bun issues for bcrypt compat.
- 09:45Found Bun#1234: bcrypt uses process.nextTick inside a CommonJS wrapper. Bun exposes it differently.
- 10:00Attempt to add polyfill: globalThis.process = { nextTick: queueMicrotask } — fails with different error
- 10:15Switch to Bun's built-in password hashing: Bun.password.hash() and Bun.password.verify()
- 10:30Redeploy. All login flows pass. Latency drops 30% due to native implementation.
We migrated a Node.js Express service to Bun for lower cold-start times. Everything passed locally with 'bun run' on my M1 Mac. The Docker image was built with Bun 1.0.1 and deployed to ECS. The health check endpoint returned 200, so I assumed all was fine. Then the first user login triggered a 500 error with 'process.nextTick is not a function'.
I traced the error to bcrypt's index.js. Bun's GitHub issue tracker confirmed that process.nextTick is not globally available in Bun—it's implemented internally but not exposed to userland. The suggested workaround was to polyfill it with queueMicrotask, but that caused a cascading failure because bcrypt also uses other Node.js globals like 'require' in a way Bun's ESM loader doesn't support.
The real fix was to replace bcrypt entirely with Bun's native password hashing API. I rewrote the auth service to use Bun.password.hash() and Bun.password.verify(). Not only did the error disappear, but the password hashing became 2x faster. The lesson: when a native addon fails in Bun, don't try to patch it—use Bun's built-in alternatives first.
Root cause
bcrypt v5.1.0 relies on process.nextTick which Bun 1.0.1 does not expose globally. Additionally, bcrypt is a native addon compiled against Node.js ABI, causing segfaults in Bun's JavaScriptCore engine.
The fix
Replaced bcrypt with Bun's built-in Bun.password.hash() and Bun.password.verify() for password hashing. Updated all import statements and removed bcrypt from package.json.
The lesson
When migrating to Bun, audit your dependency tree for native addons and Node.js-specific globals. Prefer Bun-native APIs (Bun.file, Bun.password, Bun.write) over npm packages that wrap Node.js internals.
Bun aims to be a drop-in replacement for Node.js, but its JavaScriptCore engine differs from V8. Bun implements many Node.js APIs as polyfills, but some are intentionally omitted or behave differently. For example, 'process.nextTick' exists in Bun's internal runtime but is not attached to the global 'process' object—it's accessible via 'queueMicrotask' instead.
Bun's compatibility is version-dependent. Earlier versions (pre-1.0.3) lacked 'node:buffer' and 'node:stream' imports. Bun 1.0.3+ added these but with subtle differences: 'Buffer.from()' works, but 'Buffer.allocUnsafe()' may throw due to security restrictions. Always check Bun's official compat table before assuming an API works.
Bun's module resolver prioritizes ESM over CommonJS. If a package has both 'main' and 'exports' fields, Bun will use the 'exports' field even in a CommonJS context, which can break 'require()' calls. This is a common source of 'module not found' errors that don't occur in Node.js.
To diagnose, run 'bun build --target=node --format=cjs' on your entry point. This forces CommonJS output and reveals any module resolution mismatches. You can also inspect the resolved file with 'bun run --print "require.resolve('package-name')" to see which file Bun selects.
Native addons compiled against Node.js's V8 ABI will not work in Bun's JavaScriptCore engine. This includes popular packages like 'bcrypt', 'sharp', 'node-canvas', and 'sqlite3'. When you see a segfault, illegal instruction, or a hang, it's almost always a native addon.
The fix is either to use a Bun-native alternative (e.g., 'Bun.password' for bcrypt, 'Bun.file' for fs operations) or to use a WASM-based version of the addon if available. Some packages like 'sharp' have experimental Bun support via '--bun' flag. Always check the package's issue tracker for Bun compatibility notes.
The '--bun' flag enables a compatibility mode that adds Node.js globals to the global scope. However, this flag is not a silver bullet—it may introduce performance overhead or unexpected behavior. Use it only when necessary, and prefer explicit polyfills in your entry file.
Common globals that need manual polyfilling: 'process.nextTick' (use 'queueMicrotask'), 'globalThis.Buffer' (add 'import { Buffer } from "buffer"' at top), and '__dirname' (use 'import.meta.dirname' in ESM). For a full list, consult Bun's documentation on Node.js compatibility.
Set the environment variable 'BUN_DEBUG=1' before running your app to get verbose logs from Bun's runtime. This will show module resolution steps, polyfill warnings, and any internal errors that are otherwise hidden. Combine with 'BUN_DEBUG=2' for even more detail (but beware of log flood).
Additionally, use 'bun --inspect' to attach a debugger (e.g., Chrome DevTools). This lets you step through polyfill code and see exactly which API calls fail. The debugger works best with ESM modules; for CommonJS, the call stack may be less informative.
Frequently asked questions
Why does my Node.js package work locally but fail on Bun in production?
Likely because your local environment uses Node.js's V8 engine, while Bun uses JavaScriptCore. Some Node.js APIs are not implemented in Bun, or they behave differently under load. Common culprits: missing globals (process.nextTick), native addons, and module resolution differences. Run your test suite with both runtimes to catch these early.
How do I check if a specific Node.js API is supported in Bun?
Refer to Bun's official Node.js compatibility table at bun.sh/docs/runtime/nodejs-apis. The table lists supported, partially supported, and not supported APIs per Bun version. You can also search the Bun GitHub issues for the API name—community reports often surface edge cases.
Should I use 'bun install' or 'npm install' for a Bun project?
Always use 'bun install'. Bun's package manager resolves dependencies differently—it uses a lockfile format (.bun.lockb) that ensures deterministic installs. Using npm can lead to version mismatches or missing polyfills. If you must use npm, run 'bun install' afterward to regenerate the lockfile.
What does the '--bun' flag actually do?
The '--bun' flag activates Bun's Node.js compatibility layer, which includes polyfills for common globals and module resolution shims. It changes how Bun resolves modules (e.g., prioritizing node_modules over Bun's built-in modules). However, it does not fix all compat issues—missing APIs still need explicit polyfills. Use it as a first step, but expect to add manual fixes.
Can I use Node.js native addons like 'sharp' with Bun?
Not directly. Native addons compiled against Node.js's V8 ABI will crash or hang in Bun. Some projects offer Bun-specific builds (e.g., sharp has experimental Bun support via '--bun' flag). Alternatively, use Bun's built-in APIs: 'Bun.file' for file I/O, 'Bun.password' for hashing, and 'Bun.write' for writing files. For image processing, consider using a WASM-based library like 'sharp-wasm'.