Layer 8Ehrlicher Tech-Support
SoftwareSchweregrad: AnfängerfehlerERR_MODULE_NOT_FOUND

Node.js: import ohne Dateiendung – ERR_MODULE_NOT_FOUND

In ESM scheitert import './utils' mit ERR_MODULE_NOT_FOUND. Node verlangt hier die Dateiendung – anders als require. Ursache, Beleg und Lösung.

Aufwand:
ca. 20 Min.
Gilt für:
Node.js 20, Node.js 22, Node.js 24

Das Symptom

Du fügst "type": "module" in die package.json ein, weil du endlich import statt require schreiben willst. Danach startet nichts mehr. Node bricht mit ERR_MODULE_NOT_FOUND ab und nennt eine Datei, die eindeutig existiert – ./utils, direkt daneben im selben Ordner, unübersehbar.

Der zweite Anlauf, ein Import auf ein Verzeichnis mit index.js darin, endet ähnlich unfreundlich. Die Datei ist da. Der Pfad stimmt. Node sagt trotzdem Nein.

Die „Layer 8“-Wahrheit

Node sucht nicht mehr. Das ist die ganze Änderung. Die Doku formuliert es in einem Satz, der keine Ausnahmen kennt: „A file extension must be provided when using the import keyword to resolve relative or absolute specifiers. Directory indexes (e.g. './startup/index.js') must also be fully specified.“ Kein Anhängen von .js, kein Nachschauen, ob ein Verzeichnis vielleicht eine index.js enthält. Was du schreibst, ist der Pfad – Punkt.

Was hier eigentlich weh tut, ist nicht der Fehler, sondern der Abschied von einer Gewohnheit. Jahrelang hat require('./utils') im Hintergrund eine kleine Rateschleife über Endungen und Verzeichnisse abgearbeitet, und niemand hat darüber nachgedacht. Bequem war das, billig war es nicht: Jeder Import kostete mehrere Dateisystemzugriffe, und derselbe Quelltext bedeutete je nach Verzeichnisinhalt etwas anderes. ESM verzichtet darauf bewusst, denn das Verhalten „matches how import behaves in browser environments“ – ein Browser kann nun einmal nicht in einem Verzeichnis nachsehen.

Und dann ist da die eigentliche Falle, die selten benannt wird: Es ist ein einziges Feld, das entscheidet, in welcher Welt eine .js-Datei lebt. Dateien mit der Endung .js werden als ES-Module geladen, wenn die nächstgelegene übergeordnete package.json das Feld "type" mit dem Wert "module" enthält – fehlt das Feld oder steht dort "commonjs", gelten sie als CommonJS. Wer dieses Feld setzt, ändert damit die Bedeutung jedes einzelnen Imports in allen .js-Dateien darunter. Das ist kein Schalter, das ist ein Umzug.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Klären, ob die Datei wirklich als ES-Modul läuft

    Nur dann greift die Endungspflicht. Entscheidend ist das Feld „type“ in der nächstgelegenen übergeordneten package.json, nicht eine Einstellung im Editor:

    node -p "require('./package.json').type"
    
  2. 2.

    Die Endung an relative und absolute Importe schreiben

    Die Node-Doku ist hier wörtlich: Beim import-Schlüsselwort muss für relative und absolute Angaben eine Dateiendung angegeben werden. Das gilt auch dann, wenn die Datei eindeutig ist:

    import { addTwo } from './addTwo.mjs';
    
  3. 3.

    Verzeichnis-Importe ausschreiben

    Auch Verzeichnisindizes müssen laut Doku vollständig angegeben werden. Ein Import auf ein Verzeichnis lässt den Auflöser mit einem Unsupported-Directory-Import-Fehler abbrechen, statt heimlich eine index.js zu suchen:

    import config from './startup/index.js';
    
  4. 4.

    Nicht mit dem CommonJS-Verhalten vergleichen

    In CommonJS findet require('./addTwo') die Datei addTwo.js von selbst. Diese Bequemlichkeit ist in ESM ersatzlos weg. Wer zwischen beiden Welten wechselt, sollte das als Regel merken statt als Einzelfall zu debuggen.

  5. 5.

    Bei Paketen nicht die Endung, sondern exports prüfen

    Die Endungspflicht gilt für relative und absolute Angaben. Schlägt der Import eines Paketnamens fehl, liegt es nicht an der Endung, sondern an den Einstiegspunkten, die das Paket deklariert – das ist ein anderer Fehler mit anderer Ursache.

Häufige Fragen (FAQ)

?Warum lief derselbe Import gestern noch?

Weil die Datei gestern noch als CommonJS geladen wurde. Node entscheidet das an der nächstgelegenen package.json: Steht dort „type“ auf „module“, werden .js-Dateien als ES-Module geladen, sonst als CommonJS. Ein einziges hinzugefügtes Feld kippt damit das Auflösungsverhalten aller .js-Dateien darunter.

?Kann ich Node nicht beibringen, die Endung wie früher zu raten?

Das ist die falsche Richtung. Die Pflicht zur Endung ist kein Versehen, sondern gewollt: Sie entspricht dem Verhalten von import im Browser. Wer sie umgeht, baut Code, der nur unter Node läuft – und verschiebt das Problem auf den ersten Bundler oder Browser, der ihn zu Gesicht bekommt.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

  1. [2]

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler