Layer 8Ehrlicher Tech-Support

npm install mit fester Version speichert trotzdem ein ^

npm install mit genauer Versionsangabe pinnt nichts: In der package.json landet standardmäßig ein Bereich mit ^. Was save-exact und Lockfile ändern.

Aufwand:
ca. 15 Min.
Voraussetzung:
npm (Verhalten hier geprüft mit npm 10.9.7), Ein Texteditor für package.json und .npmrc, Das eingecheckte package-lock.json
Gilt für:
npm CLI v11 (npm Docs, Seiten zuletzt bearbeitet zwischen Oktober 2023 und Februar 2026), Verhalten von save-prefix und save-exact nachgeprüft mit npm 10.9.7

Das Symptom

Ein Paket soll in genau einer Version ins Projekt, also wird sie beim Installieren ausdrücklich angegeben, Paketname, @, Versionsnummer. Damit ist die Sache erledigt, denkt man. Monate später baut jemand das Projekt frisch, ohne Lockfile oder mit einem neu erzeugten, und bekommt eine neuere Minor-Version mit leicht anderem Verhalten. Die Fehlersuche führt in die package.json, und dort steht vor der Versionsnummer ein Zirkumflex. Der Entwickler ist sicher, das nie geschrieben zu haben.

Er hat recht. Geschrieben hat es npm, beim Speichern, ohne Rückfrage, nach Voreinstellung.

Die „Layer 8“-Wahrheit

npm unterscheidet zwischen dem, was installiert wird, und dem, was in der package.json gespeichert wird. Installiert wird bei einer Versionsangabe genau diese Version; die Dokumentation von npm install sagt dazu schlicht „Install the specified version of the package.“ Gespeichert wird aber nicht die Version, sondern ein Bereich. Zuständig ist die Einstellung save-prefix, deren Standardwert das Zirkumflex ist, und die Dokumentation beschreibt genau diesen Fall am Beispiel einer Version 1.2.3: „by default its version is set to ^1.2.3 which allows minor upgrades for that package“. Bei der Prüfung für diesen Eintrag mit npm 10.9.7 ergab die Installation einer genauen Version prompt einen Eintrag mit ^ davor.

Der Denkfehler liegt in der Annahme, die Versionsangabe im Befehl sei eine Anweisung an die package.json. Sie ist nur eine Anweisung an die Installation. Wer eine exakte Version gespeichert haben will, muss das getrennt sagen, mit –save-exact: „Saved dependencies will be configured with an exact version rather than using npm’s default semver range operator.“ Die Formulierung verrät, wer hier den Standard setzt, nämlich npm und nicht der Entwickler.

Gemildert wird das Problem durch das Lockfile. Solange package-lock.json eingecheckt ist und seine Versionen die Bereiche der package.json erfüllen, nimmt npm laut Dokumentation die exakten Versionen aus dem Lockfile. Der Bereich beißt erst, wenn das Lockfile fehlt, neu erzeugt wird oder gar nicht gilt, etwa bei den Nutzern eines veröffentlichten Pakets, denn package-lock.json wird laut npm außerhalb des Wurzelprojekts ignoriert. Dass dann neuere Minor-Versionen kommen, ist kein Fehler von npm, sondern genau das, was der Bereich erlaubt. Ob diese Versionen kompatibel sind, hängt allein daran, ob ihre Autoren sich an die Empfehlungen zur semantischen Versionierung halten.

Die Lösung ist deshalb keine Reparatur, sondern eine Entscheidung. Entweder Bereiche, dann bewusst, mit eingechecktem Lockfile und npm ci im Build. Oder exakte Einträge, dann projektweit über save-exact in der .npmrc, damit nicht jeder einzelne Befehl daran denken muss.

Die echte, verifizierte Lösung

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

  1. 1.

    In der package.json nachsehen, was wirklich gespeichert wurde

    Der Blick in die package.json entscheidet, nicht die Erinnerung an den Befehl. Steht dort bei einer Abhängigkeit ein ^ vor der Version, ist das ein Bereich, der laut npm Minor-Upgrades erlaubt. Ein ~ erlaubt nur Patch-Upgrades. Nur eine nackte Versionsnummer ohne Präfix ist exakt. Bei einer Installation mit genauer Versionsangabe ohne weitere Schalter steht dort nach dem Standard von npm das Zirkumflex.

  2. 2.

    Beim Installieren ausdrücklich exakt speichern

    Der Schalter --save-exact, kurz -E, sorgt laut npm dafür, dass gespeicherte Abhängigkeiten mit exakter Version statt mit dem Standard-Bereichsoperator eingetragen werden. Bei der Prüfung für diesen Eintrag ergab derselbe Befehl ohne Schalter den Eintrag ^6.0.0 und mit Schalter 6.0.0. Ein bereits vorhandener Bereich wird durch erneutes Installieren mit dem Schalter auf die exakte Version umgeschrieben.

    npm install is-number@6.0.0 --save-exact
    
  3. 3.

    Exakt als Projektstandard festlegen

    Damit nicht jeder im Team an den Schalter denken muss, lässt sich save-exact in der projekteigenen .npmrc setzen. npm config set schreibt mit --location=project in genau diese Datei im Projektordner. Danach speichert jede Installation in diesem Projekt exakte Versionen. Die Datei gehört ins Repository, sonst gilt die Einstellung nur auf dem Rechner, auf dem sie gesetzt wurde.

    npm config set save-exact=true --location=project
    
  4. 4.

    Reproduzierbarkeit über Lockfile und npm ci sichern

    save-exact regelt nur, was in die eigene package.json geschrieben wird. Die Abhängigkeiten der Abhängigkeiten stehen dort gar nicht, festgehalten werden sie im package-lock.json. Deshalb gehört das Lockfile ins Repository, und Builds laufen mit npm ci. Laut npm setzt npm ci ein vorhandenes Lockfile voraus, bricht bei Abweichungen von der package.json mit einem Fehler ab und schreibt weder package.json noch Lockfile.

    npm ci
    
  5. 5.

    Als Paketautor an die Nutzer denken

    Wer selbst ein Paket veröffentlicht, kann sich für dessen Nutzer nicht auf das eigene Lockfile verlassen. package-lock.json kann laut npm nicht veröffentlicht werden und wird an jeder Stelle außer im Wurzelprojekt ignoriert. Bei den Nutzern zählen die Bereiche aus der veröffentlichten package.json. Ein ^ dort ist eine bewusste Zusage, Minor-Upgrades der eigenen Abhängigkeiten zu vertragen.

Häufige Fragen (FAQ)

?Ich habe eine genaue Version angegeben. Warum steht in der package.json ein ^ davor?

Weil npm die Version beim Speichern mit einem Präfix versieht, und der ist standardmäßig das Zirkumflex. Die Einstellung heißt save-prefix, ihr Standardwert ist ^, und die npm-Dokumentation beschreibt den Fall am Beispiel einer Version 1.2.3 wörtlich: „by default its version is set to ^1.2.3 which allows minor upgrades for that package“. Installiert wird dabei durchaus die angegebene Version. Gespeichert wird aber ein Bereich, der sie als Untergrenze enthält. Wer das nicht will, muss es npm ausdrücklich sagen.

?Was erlaubt ^1.2.3 genau?

Nach der npm-Dokumentation Minor-Upgrades. Die Seite zu semantischer Versionierung ordnet ^1.0.4 den Minor-Releases zu, ~1.0.4 dagegen den Patch-Releases. Ein Bereich mit ^ nimmt also neue Minor- und Patch-Versionen derselben Hauptversion mit, sobald npm die Abhängigkeit neu auflöst. Dass diese Versionen rückwärtskompatibel sind, ist eine Empfehlung von npm an die Paketautoren, keine technische Garantie. Ob sich jeder Autor daran hält, prüft npm nicht.

?Reicht nicht das package-lock.json?

Für das eigene Projekt ja, mit Bedingungen. Laut Dokumentation von npm install verwendet npm die exakten Versionen aus dem Lockfile, solange sie die Bereiche der package.json erfüllen, und npm ci schreibt weder package.json noch Lockfile, sondern bricht bei Abweichungen mit einem Fehler ab. Das Lockfile hilft aber nur, wenn es eingecheckt und benutzt wird. Und es gilt nur im Wurzelprojekt: package-lock.json kann laut npm nicht veröffentlicht werden und wird an jeder anderen Stelle ignoriert.

?Soll ich dann alles exakt festnageln?

Das ist eine Abwägung, keine Regel. Ein exakter Eintrag bedeutet, dass jede neue Version eine bewusste Änderung an der package.json ist. Ein Bereich mit ^ bedeutet, dass Minor- und Patch-Versionen beim nächsten Neuauflösen automatisch kommen. Beides ist legitim. Falsch ist nur der Zustand, in dem man glaubt, festgenagelt zu haben, und in Wahrheit einen Bereich gespeichert hat. Wer sich entscheidet, sollte es projektweit tun, zum Beispiel über die projekteigene .npmrc.

?Was ist der Unterschied zwischen ^ und ~ als Präfix?

Der Umfang der erlaubten Upgrades. Die npm-Dokumentation zu save-prefix nennt das Beispiel selbst: Mit dem Standard ^ wird aus Version 1.2.3 der Eintrag ^1.2.3, „which allows minor upgrades for that package“; nach dem Umstellen auf ~ wäre es ~1.2.3, „which only allows patch upgrades“. Die Tilde ist also enger, aber ebenfalls ein Bereich und keine feste Version. Exakt wird der Eintrag nur mit save-exact.

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. [4]
    npm Docs, CLI v11, npm-ci, Abschnitt Description

    docs.npmjs.com · abgerufen am

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

Auch zu finden unter

Verwandte Fehler

SWLästig

npm meldet Schwachstellen (vulnerabilities)

npm warnt nach der Installation vor Sicherheitslücken? Nicht jede ist gefährlich — und ein blindes npm audit fix kann mehr kaputt machen, als es behebt.