← Retour aux tutoriels

Erreur « Cannot Find Module » dans Node.js : guide complet pour la corriger

node.jsbackendcannot find modulejavascriptnpmyarnpnpmesmcommonjsdépendances

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

  1. Comprendre le message d’erreur
  2. Comment Node.js résout les modules (CommonJS et ESM)
  3. Cas les plus courants et corrections
  4. Méthode de diagnostic pas à pas
  5. Outils et commandes utiles
  6. Exemples complets (CommonJS, ESM, TypeScript)
  7. 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 :

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 :

Résolution en CommonJS (simplifiée)

Quand vous faites :

const express = require("express");

Node tente (dans les grandes lignes) :

  1. Si c’est un module “core” (fs, path, http), il le trouve immédiatement.
  2. Sinon, il cherche dans node_modules :
    • ./node_modules/express
    • puis remonte : ../node_modules/express, etc.
  3. Dans le paquet, il lit package.json :
    • main (ex: "main": "index.js")
    • sinon index.js par défaut

Quand vous faites :

const util = require("./lib/util");

Node traite ça comme un chemin de fichier et tente :

Résolution en ESM (simplifiée)

Quand vous faites :

import x from "./lib/util.js";

En ESM, Node est plus strict :

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"
}

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 :

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 :

const utils = require("./utils"); // ok si utils.js existe
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 :


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 :

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 :


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 :

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 :

{
  "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 :

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'

É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

É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 :

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 :

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 :

  1. Remplacer l’alias par un chemin relatif dans le code Node (simple et robuste)
  2. Utiliser un mécanisme Node natif (imports dans package.json) ou un outil de réécriture des paths à la compilation
  3. 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

2) Être explicite en 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 :

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


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.