Skip to content
Latchkey

Jest "ReferenceError: TextEncoder is not defined" (jsdom)

Code under test referenced TextEncoder (or TextDecoder, crypto.subtle, ResizeObserver), a Web API the jsdom environment does not provide. Referencing it throws unless you polyfill it in setup.

What this error means

A test throws ReferenceError: TextEncoder is not defined, often from a dependency that encodes bytes (uuid, a crypto lib). The same code runs in a real browser; jsdom simply omits the global.

jest
ReferenceError: TextEncoder is not defined

   6 | import { v4 as uuid } from 'uuid';
   7 |
 > 8 | const enc = new TextEncoder();
     |             ^

Diagnose it: flake, environment, or genuine failure?

Before debugging the assertion, establish whether the test is deterministic. A test that fails only in CI is usually order-dependent, time-dependent, or racing something, and fixing the assertion will not help.

Terminal
# does it fail in isolation?
npx vitest run path/to/file.test.ts

# is it order dependent? run the suite in a random order twice
npx vitest run --sequence.shuffle

# is it a race? run the same file repeatedly
for i in $(seq 1 20); do npx vitest run path/to/file.test.ts || break; done

Common causes

jsdom does not implement the API

jsdom omits some Web APIs (TextEncoder, crypto.subtle, ResizeObserver, fetch on older setups). Referencing them throws a ReferenceError.

Wrong environment for the code

Under the default node environment there is no window/document; under jsdom some Node globals are also absent, so the right environment plus a polyfill is needed.

How to fix it

Polyfill the missing global in setup

Provide the API from Node in a setup file referenced by setupFiles.

jest.setup.js
// jest.setup.js
import { TextEncoder, TextDecoder } from 'util';
global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;

Wire the setup file into Jest

jest.config.js
// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFiles: ['<rootDir>/jest.setup.js'],
};

CI-only causes worth ruling out

  • Runners have fewer cores than a laptop, so timing-sensitive tests that pass locally fail under contention.
  • No TTY and a different locale or timezone. Snapshot tests containing formatted dates or numbers are the usual casualty; pin TZ and LANG in the job.
  • Parallel workers sharing a database, a port, or a temp directory. Give each worker its own namespace.
  • Default timeouts calibrated on a fast machine. A cold runner is slower on first execution, especially before any cache warms.

How to prevent it

  • Keep polyfills in a shared setupFiles module.
  • Pick jsdom for DOM suites and node for pure-logic suites.
  • Pin jest-environment-jsdom, a separate package since Jest 28.

Frequently asked questions

What causes Jest "ReferenceError: TextEncoder is not defined" (jsdom)?
There are 2 common causes: jsdom does not implement the api and wrong environment for the code. jsdom omits some Web APIs (TextEncoder, crypto.subtle, ResizeObserver, fetch on older setups).
How do I fix Jest "ReferenceError: TextEncoder is not defined" (jsdom)?
There are 2 fixes depending on which cause you have: polyfill the missing global in setup and wire the setup file into jest. Work through them in order, since the first is the most common.
What does Jest "ReferenceError: TextEncoder is not defined" (jsdom) actually mean?
A test throws ReferenceError: TextEncoder is not defined, often from a dependency that encodes bytes (uuid, a crypto lib).
How do I stop Jest "ReferenceError: TextEncoder is not defined" (jsdom) happening again?
Keep polyfills in a shared setupFiles module. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card