I recently hit a confusing CI failure after opening a PR at work.

SyntaxError: Export named 'createClient'
not found in module './server'

The confusing part was that the export clearly existed. The test passed when I ran the file individually and as part of the full monorepo test suite locally, but failed when CI ran against my PR.

The Problem

In my codebase, a lot of tests mocked the same module:

mock.module('./server', () => ({
  myExport: mock(),
}));

With Bun, mock.module() replaces the module’s exports, but it does not merge the mock with the original module.

That means the mocked module above only exports myExport. If another test imports createClient while that mock is active, Bun reports that the export doesn’t exist (even though it exists in the real source file).

The error was describing the mocked module, not the real one.

Why it only failed in CI

Bun’s test runner uses shared module state, so tests that mock the same module can interfere with one another (similar issue encountered in Cross-File Mock Leakage article).

Locally, the module leading order happened to hide the conflict. In the CI, a different runtime environment exposed the partial mock before my test imported createClient.

Differences in module loading order, Bun versions, and CI Config can reveal shared-state problems that do not consistently reproduce locally.

My first attempted fix

My initial solution was to preserve the real exports and override only the function needed by the test:

const original = {
  ...(await import('./server')),
};
 
const createClient = mock();
 
mock.module('./server', () => ({
  ...original,
  createClient,
}));

This looked reasonable, and it passed locally. But, it did not fully solve the problem.

Another test could still register a partial mock for the same module afterward. Because the module state was shared, my test remained vulnerable to whichever mock became active.

I could have updated every test with a complete mock, but that would’ve made them harder to maintain. Every new export from server.ts could require updating several unrelated tests.

The Solution

I looked at what the test actually needed to verify. It was testing some data-processing behavior, not the server module itself.

I moved that behavior into a small function whose external dependency could be passed as an argument:

async function processData(loadData: () => Promise<Data>)
{
	const data = await loadData();
	return transform(data);
}

Production code provides the real dependency:

await processData(() => client.load());

The test provides a mock directly:

const loadData = mock().mockResolvedValue(testData);
const result   = await processData(loadData);
 
expect(result).toEqual(expectedData);

The test no longer mocks the shared server module, so another test cannot silently replace its dependencies.

Bottom Line

When an error says an export is missing, check the active module mock, not just the source file. A partial mock may have replaced the export you were looking for.

And when module mocks become order-dependent, a more elaborate mock only hide the underlying problem. Separating the behavior from its external dependency often produces a simpler and more reliable test.

References

Here are some references I used to fix my issue and write this article.