Diagnosing Common Bun Test Runner Failures: A Step‑by‑Step Guide
A diagnostic guide for Bun's test runner covering hanging tests, missing files, stripped type imports, ES module conflicts, and CI port leaks—with cause tables, ordered checks, concrete fixes, and escalation steps.
29 Aug 2026, 11:15 UTC

Recognizable Conditions
When bun test behaves unexpectedly, the symptom usually falls into one of five patterns. Identify the pattern first, then follow the matching diagnostic row.
| Symptom | Typical Cause | Quick Test |
|---|---|---|
| Tests hang or time out after a few seconds | Missing or mis‑configured environment variables (e.g. process.env.NODE_UNDEFINED) causing an infinite loop in async setup/teardown |
Run bun test --log-level=debug and watch for repeated console.timeStamp markers |
No test files found reported |
Glob pattern mismatch or test files placed outside default src/**/*.test.{js,ts} |
Run bun test --list to see resolved file list |
Assertion errors with undefined where a mock is expected |
Bun’s transpiler strips type‑only imports; import type { … } removes the runtime value |
Inspect compiled output with bun build --outdir ./tmp and verify the import remains |
Failed to load ES module and exit code 1 |
CommonJS require() used in a file treated as ES module because "type": "module" in package.json |
Check package.json and rename file to .cjs or switch to import |
Tests pass locally but fail in CI with Address already in use |
Leftover server processes from previous runs; teardown hook omitted or threw | Add explicit afterAll that calls server.close() and verify with lsof -i :PORT in CI logs |
Ordered Diagnostic Checks
- Run the test suite with debug logging –
bun test --log-level=debug(run in the repository root, no elevated permissions). Look for stalled timestamps or repeated hook entries. - List discovered test files –
bun test --list. Compare the output with your expected test locations. If files are missing, adjust the glob inbunfig.tomlor move files undersrc/. - Verify module type handling – Execute
bun build --outdir ./tmpon a failing test file. Open the emitted.jsand confirm thatrequirecalls are absent when"type": "module"is set. - Check for lingering processes – In CI, after a test run, run
lsof -i :(orss -ltnp) to see if the test server port is still bound. Absence of a properafterAllhook is the usual culprit. - Inspect environment variables – Add
console.log(process.env.NODE_UNDEFINED)at the top of the test setup file. Run a single test (bun test path/to/file.test.ts) to confirm the variable is defined.
Fixes Tied to Findings
1. Hanging Tests – Missing Env Var
// test/setup.ts
if (!process.env.NODE_UNDEFINED) {
process.env.NODE_UNDEFINED = '1';
}
Add the guard before any async initialization. Re‑run bun test --log-level=debug and verify the timestamps progress past the setup hook.
2. No Test Files Found – Glob Mis‑match
Edit bunfig.toml (create if missing) and set:
[test]
root = "."
pattern = ["**/*.test.{js,ts,jsx,tsx}"]
Run bun test --list again; the list should now include all intended files.
3. Stripped Type‑Only Imports
Replace import type { mockFn } from './mocks' with a runtime import:
import { mockFn } from './mocks';
// or
import { mockFn as mockFnType } from './mocks';
Confirm with bun build --outdir ./tmp that the import appears in the output.
4. ES Module / CommonJS Conflict
Option A – Rename the file to .cjs (e.g., legacy.cjs) and keep require.
Option B – Change package.json to "type": "commonjs" if the whole project can stay CommonJS.
Option C – Convert the file to ESM syntax (import … from …). Choose the option that matches the rest of the codebase.
5. Port Leak in CI
// test/server.test.ts
import { afterAll } from 'bun:test';
let server: ReturnType;
beforeAll(() => {
server = Bun.serve({ fetch: () => new Response('ok') });
});
afterAll(() => {
server.stop(); // ensures the port is released
});
Run the suite locally and in CI; verify with lsof -i : that the port is free after the run.
Escalation Criteria
- If after applying the matched fix the symptom persists across three consecutive runs, capture the full debug log (
bun test --log-level=debug 2>&1 | tee debug.log) and file an issue on the Bun repository with the log attached. - When multiple symptoms appear simultaneously (e.g., hanging tests and ES module errors), treat each independently; do not assume a single root cause.
- If the test suite relies on
--paralleland exhibits flakiness, note that Bun’s test runner does not yet support true parallel worker execution. Disable--parallelas a workaround and report the limitation.
Limitations & Practical Verification
- Bun’s test runner currently runs tests sequentially; parallel execution flags may produce nondeterministic ordering.
- SQLite in‑memory databases must be instantiated per test (
new Database(':memory:')) to avoid cross‑test contamination. - All commands assume execution from the project root with a standard user account; no
sudorequired. - Verification: after each fix, run the specific diagnostic command again (e.g.,
bun test --listfor glob issues) and confirm the expected output appears.
Quick Reference Cheat Sheet
# Debug hang
bun test --log-level=debug
# List files
bun test --list
# Inspect compiled output
bun build --outdir ./tmp
# Check port usage (Linux/macOS)
lsof -i :3000
# Run single test file
bun test path/to/file.test.ts
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.