Next.js

Fix Module not found: Can't resolve 'encoding' in Vercel

You see "Module not found: Can't resolve 'encoding'" in your Vercel deployment logs. This guide explains why it happens with cross-fetch/node-fetch and how to fix it permanently.

Mahdi Ben RhoumaMahdi Ben Rhouma
Updated
8 min read

Your Vercel deployment log stops on this line:

plaintext
Module not found: Can't resolve 'encoding' in '/vercel/path0/node_modules/cross-fetch/node_modules/node-fetch/lib'

The build fails before the app ever deploys. You'll typically hit it in a Next.js app that uses the Supabase JS client, because @supabase/supabase-js transitively depends on cross-fetch. The error appears during the Next.js build process — either locally with next build or in Vercel's CI logs — and the behavior is the same across Next.js 13+, Vercel deployments, and projects using @supabase/supabase-js. It does not occur in pure Node.js environments where encoding is more likely to be present.

The short version: cross-fetch depends on node-fetch@2, and node-fetch@2 has an optional dependency on the encoding package that Webpack still tries to statically resolve at build time. Since encoding isn't installed by default, that resolution fails and Next.js's production build reports it as a hard error. Installing encoding (or telling Webpack to stop looking for it) fixes it — upgrading cross-fetch does not, because it still ships on node-fetch@2 as of cross-fetch@4.1.0 (verified against the published package.json on npm, September 2026).

The real dependency chain: cross-fetch → node-fetch@2 → an optional require('encoding')#

cross-fetch (a popular polyfill for window.fetch) depends on node-fetch@2.x. Inside node-fetch@2's lib/body.js, the only place encoding is referenced is this line, guarded by a try/catch specifically so the module can run without it:

js
// node-fetch/src/body.js (2.x branch)
let convert;
try { convert = require('encoding').convert; } catch(e) {}

That convert function is used exclusively by response.textConverted() — a rarely-called method that decodes a response body using a non-UTF-8 charset. A normal fetch(...).then(r => r.json()) or r.text() call never touches it. In node-fetch@2's own package.json, encoding is declared as an optional peer dependency (peerDependenciesMeta: { encoding: { optional: true } }), precisely because most consumers never need it.

The problem is that Webpack parses require() calls statically, even ones wrapped in try/catch. It doesn't know at bundle time that the catch block makes a missing encoding harmless — it just sees an unresolved require('encoding') and reports "Module not found." Next.js's production build (next build) treats that as a fatal compilation error rather than a soft warning, which is why the deploy fails even though the code path that actually needs encoding is never reached.

This is not a bug in Supabase or cross-fetch — it's a side effect of bundling a Node.js package (node-fetch@2) that has an unresolved optional dependency, inside a bundler (Webpack) that can't tell "optional" from "required" just by reading the source.

Why it fails on Vercel but not on your machine#

Vercel's build environment enforces stricter module resolution than local dev servers. Locally, next dev may cache or skip some checks, but next build (and Vercel's CI) will catch the missing dependency. That's why the first sign of trouble is often a red deployment, not a local error — and why running next build locally is the fastest way to reproduce it before pushing.

The one package.json change that resolves it#

The reliable fix is to install encoding explicitly so the require('encoding') inside node-fetch@2 actually resolves:

json
{
  "dependencies": {
    "encoding": "^0.1.13"
  }
}

This works because it gives Webpack a real package to find — node-fetch@2's try/catch around require('encoding') was always safe to run without it, but Webpack needs the module to exist on disk to stop erroring during the build.

Bumping cross-fetch does not fix this. It's tempting to assume a newer cross-fetch pulls a newer node-fetch that drops the encoding reference, but that's not what the published packages do: cross-fetch@4.1.0 (the latest release as of September 2026) still depends on node-fetch@^2.7.0 — every 4.x release of cross-fetch, including the 4.0.0 stable and its alphas, has shipped on node-fetch@2. There is no cross-fetch version that moves you to node-fetch@3. If you're chasing this error by bumping cross-fetch, stop — it won't change the outcome; installing encoding (or disabling it in Webpack, next section) is what actually resolves the build.

Concretely:

  1. Open package.json.
  2. Add "encoding": "^0.1.13" to dependencies.
  3. Run npm install or yarn install.
  4. Restart your dev server with next dev or run next build.

If you're using Yarn, ensure you run yarn install — not just yarn add — to avoid version conflicts in the lockfile.

Is encoding safe to ship? Yes — it's a small, well-maintained package (20+ years old) used by many Node.js libraries. But in browser bundles, it's better to disable it via Webpack (encoding: false), since modern browsers have native TextEncoder. That's the next section.

If your app only runs in Node.js (e.g., server-side rendering), installing encoding alone may suffice. For full-stack Next.js apps, you must also configure Webpack to avoid client-side bundle errors.

encoding installed, Webpack still failing: check resolve.fallback#

Sometimes encoding is present, but Next.js's Webpack configuration still excludes it due to strict externals or resolve.fallback settings. This happens if you've customized next.config.js — see the official Next.js custom webpack config docs for how the webpack() function and its config argument work.

To diagnose, check next.config.js for:

js
// next.config.js
module.exports = {
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        fs: false,
        net: false,
        tls: false,
      };
    }
    return config;
  },
};

If encoding is missing from fallback, add it:

js
// next.config.js
module.exports = {
  webpack: (config, { isServer }) => {
    config.resolve.fallback = {
      ...config.resolve.fallback,
      encoding: false, // ← explicitly disable polyfill for encoding
    };
    return config;
  },
};

Why false? Because encoding is only needed at runtime in Node.js environments (like server-side rendering), not in the browser. Setting it to false tells Webpack to ignore it in client bundles, avoiding the error while keeping SSR safe.

Where this error actually comes from in a Supabase project today#

If you're hitting this error via @supabase/supabase-js, check your installed version first. As of the current @supabase/supabase-js (v2.117.0, verified against its published package.json on npm), none of its sub-packages — @supabase/auth-js, @supabase/postgrest-js, @supabase/storage-js, @supabase/functions-js, or @supabase/realtime-js — depend on cross-fetch or node-fetch anymore; they use the runtime's native fetch. So on a current install, this specific error is most often coming from a different dependency in your tree that still uses cross-fetch or node-fetch@2 directly (isomorphic-fetch and several older HTTP client wrappers do), or from a pinned/legacy Supabase client version.

Run npm ls cross-fetch (or npm ls node-fetch) to find which package actually pulls it in, then apply the same fix — install encoding, or disable it in Webpack (next section) — regardless of which dependency is the source. If you need to force every copy in the tree to a specific version, npm's own mechanism is the overrides field in package.json (npm 8.3+); Yarn's equivalent is resolutions. Neither one removes the need to install encoding, since (as shown above) the versions available still depend on node-fetch@2.

Confirming the build is clean#

Run:

bash
npm run build

You should see:

text
✓ Ready in X.Xs
✓ Creating an optimized production build
✓ Compiled successfully

Instead of the Module not found: Can't resolve 'encoding' error. Once the local build passes, the Vercel build will pass too — the CI runs the same next build step.

A next.config.js that prevents the whole class of errors#

Next.js optimizes for the browser by default, stripping Node.js polyfills unless explicitly requested. The encoding package is one of many Node.js-specific modules that don't exist in browsers — others include buffer, process, and stream. For projects that need broader Node.js core API coverage in the browser, the node-polyfills Webpack plugin automatically maps Node built-ins (like crypto, path, and stream) to browser-compatible shims, though for this specific case a targeted resolve.fallback entry is enough.

Two practices keep this from recurring:

  1. Install encoding as soon as you add any dependency built on node-fetch@2 (directly or transitively, cross-fetch included). There is no currently published version of cross-fetch that avoids node-fetch@2, so swapping versions won't save you — installing the package will.

  2. Configure Webpack polyfills explicitly. In next.config.js, always include encoding: false in resolve.fallback for client bundles. This tells Webpack to skip trying to resolve it in the browser bundle, avoiding the resolution error without needing the package installed at all.

Here's a production-ready next.config.js snippet:

js
// next.config.js
const nextConfig = {
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        fs: false,
        net: false,
        tls: false,
        encoding: false,
      };
    }
    return config;
  },
};
 
module.exports = nextConfig;

With encoding installed and the Webpack fallback configured, the deployment builds cleanly and stays that way — no cross-fetch version bump required. I cover this pattern — along with Supabase-specific optimizations like connection pooling and environment variable handling — in Deploying Next.js + Supabase to Production.

Further reading: Fix ChunkLoadError: Loading Chunk Failed in Next.js, Fix Dynamic Server Usage Error in Next.js App Router.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.