Erreur « Cannot Find Module » dans Node.js : guide complet pour la corriger
L’erreur Error: Cannot find module '...' est l’une des plus fréquentes dans l’écosystème Node.js. Elle apparaît quand Node (ou un bundler/outillage qui s’appuie sur la résolution de modules) n’arrive pas à localiser un module que votre code tente d’importer.
Ce guide explique en profondeur comment fonctionne la résolution de modules, les causes réelles de l’erreur, et une méthode systématique pour la diagnostiquer et la corriger, avec des commandes concrètes.
Sommaire
- Comprendre le message d’erreur
- Comment Node.js résout les modules (CommonJS et ESM)
- Cas les plus courants et corrections
- 1) Dépendance non installée
- 2) Mauvais nom de paquet / faute de frappe
- 3) Mauvais chemin relatif
- 4) Extension et ESM (
.js,.mjs,.cjs) - 5)
type: "module"et incompatibilités CJS/ESM - 6)
exports/maindanspackage.jsond’un paquet - 7) Monorepos / workspaces (npm, pnpm, Yarn)
- 8) Modules globaux vs locaux
- 9) Cache, lockfile,
node_modulescorrompu - 10) Alias TypeScript / chemins de bundler non compris par Node
- 11) Environnements Docker/CI et chemins différents
- Méthode de diagnostic pas à pas
- Outils et commandes utiles
- Exemples complets (CommonJS, ESM, TypeScript)
- Prévenir l’erreur à l’avenir
Comprendre le message d’erreur
Un message typique :
node index.js
# Error: Cannot find module 'express'
# Require stack:
# - /chemin/vers/projet/index.js
Ou en ESM :
node app.mjs
# Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'chalk' imported from /.../app.mjs
Ce que cela signifie réellement
Node vous dit : “Je ne sais pas où se trouve ce module”. Cela peut vouloir dire :
- le paquet n’est pas installé dans
node_modules - le nom du paquet est incorrect
- le chemin relatif vers un fichier est faux
- vous utilisez ESM et vous avez oublié une extension
- le paquet a une configuration
exportsqui empêche certains imports - vous êtes dans un monorepo et vous exécutez la commande depuis le mauvais dossier
- votre outil (TypeScript, bundler) résout des alias que Node ne comprend pas
La clé : Node ne “devine” pas. Il suit des règles strictes.
Comment Node.js résout les modules (CommonJS et ESM)
Il existe deux grands systèmes de modules :
- CommonJS (CJS) :
require(...),module.exports(historiquement le plus courant) - ECMAScript Modules (ESM) :
import ... from ...,export ...(standard moderne)
Résolution en CommonJS (simplifiée)
Quand vous faites :
const express = require("express");
Node tente (dans les grandes lignes) :
- Si c’est un module “core” (
fs,path,http), il le trouve immédiatement. - Sinon, il cherche dans
node_modules:./node_modules/express- puis remonte :
../node_modules/express, etc.
- Dans le paquet, il lit
package.json:main(ex:"main": "index.js")- sinon
index.jspar défaut
Quand vous faites :
const util = require("./lib/util");
Node traite ça comme un chemin de fichier et tente :
./lib/util./lib/util.js./lib/util.json./lib/util.node./lib/util/index.js, etc.
Résolution en ESM (simplifiée)
Quand vous faites :
import x from "./lib/util.js";
En ESM, Node est plus strict :
- les extensions sont généralement requises (
.js,.mjs,.cjs) - les règles autour de
package.json(exports,type) sont plus contraignantes
Pour un paquet :
import chalk from "chalk";
Node va chercher le paquet dans node_modules, puis suivre exports (si présent) ou main/module selon les cas.
type: "module" : pourquoi ça change tout
Dans package.json :
{
"type": "module"
}
- Les fichiers
.jssont interprétés comme ESM - Pour du CommonJS, il faut utiliser
.cjsou retirertype: "module"
Cas les plus courants et corrections
1) Dépendance non installée
Symptôme :
Error: Cannot find module 'express'
Vérifiez si le paquet est installé :
npm ls express
S’il n’apparaît pas, installez-le :
npm install express
Ou avec pnpm/yarn :
pnpm add express
yarn add express
Si c’est une dépendance de développement (tests, lint, build) :
npm install -D vitest
Ensuite relancez :
node index.js
2) Mauvais nom de paquet / faute de frappe
Exemples fréquents :
require("Express")au lieu derequire("express")(Linux/macOS sensibles à la casse)- confusion entre paquets similaires (
node-fetchvsfetch,lodash-esvslodash)
Vérifiez le nom exact :
npm view express name version
Ou cherchez :
npm search express
3) Mauvais chemin relatif
Symptôme :
Error: Cannot find module './utils'
Dans CommonJS, ./utils peut fonctionner si ./utils.js existe. En ESM, il faut souvent l’extension.
Vérifiez votre arborescence :
ls -la
find . -maxdepth 2 -type f | sed -n '1,120p'
Corrigez l’import :
- CJS :
const utils = require("./utils"); // ok si utils.js existe
- ESM :
import utils from "./utils.js"; // extension explicite
Attention au point de départ : ./ est relatif au fichier courant, pas au dossier où vous lancez node.
4) Extension et ESM (.js, .mjs, .cjs)
Erreur typique en ESM :
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/.../utils' imported from /.../app.js
Cause : import sans extension.
Solution : ajoutez l’extension :
import { helper } from "./utils.js";
Ou renommez vos fichiers :
.mjspour ESM explicite.cjspour CommonJS explicite
5) type: "module" et incompatibilités CJS/ESM
Cas classique : vous êtes en ESM (type: "module") mais vous utilisez require :
const express = require("express");
Vous verrez plutôt une erreur du type require is not defined in ES module scope, mais cela peut se combiner avec des problèmes de résolution.
Solutions :
Option A — passer en ESM :
import express from "express";
Option B — rester en CJS :
- renommer le fichier en
.cjs, ou - enlever
"type": "module"danspackage.json
Option C — importer un module CJS depuis ESM :
import pkg from "some-cjs-package";
const { something } = pkg;
Ou via createRequire :
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const cjsOnly = require("cjs-only-package");
6) exports / main dans package.json d’un paquet
Beaucoup de paquets modernes définissent exports :
{
"name": "mypkg",
"exports": {
".": "./dist/index.js"
}
}
Si vous tentez :
import x from "mypkg/dist/internal.js";
Node peut refuser :
ERR_PACKAGE_PATH_NOT_EXPORTED
Parfois, selon l’outillage, cela se manifeste comme un “cannot find module”.
Solution :
- importer uniquement les chemins publics :
import x from "mypkg"; - ou vérifier la doc du paquet pour les sous-chemins autorisés :
cat node_modules/mypkg/package.json
7) Monorepos / workspaces (npm, pnpm, Yarn)
Dans un monorepo :
repo/
package.json (workspaces)
packages/
api/
package.json
web/
package.json
Si vous lancez node depuis le mauvais dossier, Node résout node_modules différemment.
Vérifiez où vous êtes :
pwd
ls
Installez au bon endroit :
-
npm workspaces :
npm install npm --workspace packages/api install express -
pnpm :
pnpm -r install pnpm --filter api add express -
yarn :
yarn install yarn workspace api add express
Vérifiez la dépendance dans le bon package.json.
8) Modules globaux vs locaux
Installer un paquet globalement :
npm install -g typescript
…ne le rend pas disponible pour require("typescript") dans votre projet (sauf cas particuliers). Node résout d’abord localement.
Si votre code fait :
const ts = require("typescript");
Installez-le dans le projet :
npm install typescript
Pour exécuter un binaire sans l’installer globalement, préférez :
npx tsc --version
9) Cache, lockfile, node_modules corrompu
Parfois, node_modules est incohérent (installation interrompue, conflits, changement de Node, etc.).
Procédure de nettoyage (npm) :
rm -rf node_modules package-lock.json
npm cache verify
npm install
Avec pnpm :
rm -rf node_modules pnpm-lock.yaml
pnpm store prune
pnpm install
Avec yarn :
rm -rf node_modules yarn.lock
yarn cache clean
yarn install
Ensuite relancez votre script :
npm run start
10) Alias TypeScript / chemins de bundler non compris par Node
Vous pouvez avoir dans tsconfig.json :
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
Et dans votre code :
import { foo } from "@/foo";
TypeScript (ou votre bundler) peut comprendre cet alias, mais Node en runtime ne le comprend pas, d’où un Cannot find module '@/foo'.
Solutions possibles :
- Compiler en remplaçant les chemins (selon votre chaîne d’outils)
- Utiliser un loader/runtime qui gère les paths (ex:
ts-node+ config adéquate) - Éviter les alias côté Node pur, ou utiliser des imports relatifs
- Avec ESM, utiliser
importsdanspackage.json(Node >= 16/18 selon usage) :
{
"type": "module",
"imports": {
"#src/*": "./src/*"
}
}
Puis :
import { foo } from "#src/foo.js";
11) Environnements Docker/CI et chemins différents
En Docker, un oubli de copie peut provoquer l’erreur :
- vous copiez
package.jsonmais paspackage-lock.json - vous n’exécutez pas
npm install - vous montez un volume qui écrase
node_modules
Exemple de Dockerfile robuste :
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["node", "index.js"]
En CI, utilisez npm ci (reproductible) :
npm ci
npm test
Méthode de diagnostic pas à pas
Voici une checklist efficace quand l’erreur survient.
Étape 1 — Lire exactement le module manquant
Exemple :
Cannot find module './config'
- Si ça commence par
./ou../ou/: chemin de fichier - Sinon : paquet npm (ou module core)
Étape 2 — Vérifier si c’est un module core
Si vous importez fs, path, http, node:fs, etc., Node devrait les trouver.
En ESM moderne, préférez :
import fs from "node:fs";
Si vous avez Cannot find module 'fs', c’est souvent un environnement non-Node (ex: bundler côté navigateur) ou une config étrange.
Étape 3 — Pour un paquet : vérifier l’installation
npm ls <nom-du-paquet>
Si vide :
npm install <nom-du-paquet>
Étape 4 — Vérifier le bon dossier d’exécution
Le node_modules utilisé dépend de l’emplacement du fichier exécuté et de la hiérarchie de dossiers.
Affichez :
pwd
node -p "process.cwd()"
node -p "process.version"
Étape 5 — Inspecter la résolution réelle
En CommonJS, vous pouvez demander à Node où il résout un module :
node -p "require.resolve('express')"
Pour un chemin relatif :
node -p "require.resolve('./utils', { paths: [process.cwd()] })"
En ESM, c’est moins direct, mais vous pouvez tester via un petit script ou vérifier l’existence des fichiers.
Étape 6 — Vérifier ESM/CJS et extensions
- Avez-vous
"type": "module"?cat package.json - Vos imports ESM ont-ils des extensions ?
- Vos fichiers sont-ils
.cjs/.mjscorrectement ?
Étape 7 — Vérifier exports du paquet
cat node_modules/<paquet>/package.json
Cherchez exports, main, module.
Outils et commandes utiles
Voir l’arbre des dépendances
npm ls
npm ls --depth=0
Identifier la version installée
npm ls lodash
node -p "require('lodash/package.json').version"
Réinstaller proprement (npm)
rm -rf node_modules package-lock.json
npm install
Installation reproductible en CI
npm ci
Vérifier les fichiers présents
ls -la node_modules/<paquet>
ls -la src
Activer des logs (selon contexte)
Pour Node, il n’existe pas un “mode debug résolution” universel aussi simple, mais vous pouvez :
- inspecter
require.resolve - imprimer
module.paths(CommonJS) :
node -e "console.log(module.paths)"
Exemples complets (CommonJS, ESM, TypeScript)
Exemple A — Projet CommonJS simple
Structure :
proj/
package.json
index.js
package.json :
{
"name": "proj",
"version": "1.0.0",
"main": "index.js"
}
index.js :
const express = require("express");
const app = express();
app.get("/", (req, res) => res.send("OK"));
app.listen(3000);
Si vous exécutez :
node index.js
Et obtenez Cannot find module 'express', corrigez :
npm install express
node index.js
Exemple B — Projet ESM avec extensions
Structure :
esm-proj/
package.json
src/
app.js
util.js
package.json :
{
"name": "esm-proj",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node src/app.js"
}
}
src/util.js :
export function hello(name) {
return `Bonjour ${name}`;
}
src/app.js :
import { hello } from "./util.js"; // extension obligatoire (recommandé)
console.log(hello("Node"));
Lancez :
npm run start
Si vous écrivez import { hello } from "./util", vous risquez ERR_MODULE_NOT_FOUND.
Exemple C — TypeScript : alias et runtime
Supposons :
- vous compilez TypeScript vers
dist/ - vous utilisez un alias
@/
tsconfig.json :
{
"compilerOptions": {
"outDir": "dist",
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
src/index.ts :
import { foo } from "@/foo";
console.log(foo());
Problème : après compilation, Node exécute dist/index.js mais ne sait pas résoudre @/foo.
Solutions typiques :
- Remplacer l’alias par un chemin relatif dans le code Node (simple et robuste)
- Utiliser un mécanisme Node natif (
importsdanspackage.json) ou un outil de réécriture des paths à la compilation - Utiliser un bundler (esbuild, vite, webpack) pour produire un bundle résolu
Commandes de base :
npm install -D typescript
npx tsc
node dist/index.js
Si l’erreur survient, elle est liée à la résolution runtime, pas à l’installation.
Prévenir l’erreur à l’avenir
1) Verrouiller et reproduire les installations
- Utilisez
package-lock.json/pnpm-lock.yaml/yarn.lock - En CI :
npm ciplutôt quenpm install
2) Être explicite en ESM
- Ajoutez les extensions dans les imports locaux :
./util.js - Choisissez clairement ESM ou CJS :
- ESM :
"type": "module"+import - CJS : pas de
"type": "module"+require, ou fichiers.cjs
- ESM :
3) Éviter les imports internes non publics
N’importez pas somepkg/dist/... sauf si la doc le recommande. Préférez l’API publique.
4) Vérifier les workspaces
Dans un monorepo :
- installez les dépendances au bon niveau
- exécutez les scripts depuis le bon package
- documentez les commandes (
README)
5) Ajouter des scripts de vérification
Exemples :
{
"scripts": {
"clean": "rm -rf node_modules",
"reinstall": "rm -rf node_modules package-lock.json && npm install",
"start": "node index.js"
}
}
Résumé rapide des corrections typiques
- Paquet manquant →
npm install <paquet> - Mauvais nom / casse → corriger l’import
- Chemin relatif faux → corriger
./../et vérifier l’arborescence - ESM → ajouter extensions (
./file.js) et vérifier"type": "module" - Monorepo → installer/exécuter dans le bon workspace
- Alias TS/bundler → Node ne les résout pas sans configuration dédiée
node_modulescassé → supprimernode_modules+ lockfile puis réinstaller
Si vous me copiez/collez le message d’erreur complet (avec le Require stack ou le fichier importeur), votre arborescence (même partielle) et votre package.json, je peux vous indiquer la cause exacte et la correction la plus propre pour votre cas.