[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-post-npm-pruned-your-lockfile-to-your-laptop":3},{"name":4,"created_at":5,"published_at":6,"updated_at":7,"id":8,"uuid":9,"content":10,"slug":161,"full_slug":162,"sort_by_date":163,"position":164,"tag_list":165,"is_startpage":166,"parent_id":167,"meta_data":163,"group_id":168,"first_published_at":6,"release_id":163,"lang":169,"path":163,"alternates":170,"default_full_slug":163,"translated_slugs":163},"npm pruned your lockfile to your laptop","2026-09-03T12:59:31.419Z","2026-09-03T12:59:31.459Z","2026-09-03T12:59:31.473Z",216067159759048,"548636cd-226f-4bcf-b35a-b1e385ae90b2",{"_uid":11,"body":12,"title":4,"excerpt":158,"component":159,"publish_at":160},"b1f845bf-4f20-44f8-a648-a59b362f3188",{"type":13,"content":14},"doc",[15,21,26,37,44,54,58,63,67,95,99,104,114,119,135,139,143],{"type":16,"content":17},"paragraph",[18],{"text":19,"type":20},"A green build, one dependency added, and then this on CI:","text",{"type":22,"content":23},"code_block",[24],{"text":25,"type":20},"npm error `npm ci` can only install packages when your package.json\nnpm error and package-lock.json are in sync.\nnpm error Missing: @emnapi\u002Fcore@1.11.3 from lock file\nnpm error Missing: @emnapi\u002Fruntime@1.11.3 from lock file",{"type":16,"content":27},[28,30,35],{"text":29,"type":20},"I had never heard of ",{"text":31,"type":20,"marks":32},"@emnapi\u002Fcore",[33],{"type":34},"code",{"text":36,"type":20},". I had not installed it. It is a transitive dependency of a WebAssembly build of a native tool, several levels down.",{"type":38,"attrs":39,"content":41},"heading",{"level":40},2,[42],{"text":43,"type":20},"What npm had done",{"type":16,"content":45},[46,48,52],{"text":47,"type":20},"Native packages ship one build per platform as optional dependencies. Your lockfile is supposed to list all of them, so an install on any machine can pick the right one. When ",{"text":49,"type":20,"marks":50},"npm install",[51],{"type":34},{"text":53,"type":20}," runs on a Mac it resolves the tree for a Mac, and it writes that pruned tree back to the lockfile. The Linux and Windows branches are dropped.",{"type":16,"content":55},[56],{"text":57,"type":20},"Commit it, and CI on Linux asks for a package the lockfile no longer mentions.",{"type":38,"attrs":59,"content":60},{"level":40},[61],{"text":62,"type":20},"The damage is cumulative",{"type":16,"content":64},[65],{"text":66,"type":20},"This is the part that surprised me. I counted the platform-specific entries for one package as I went:",{"type":68,"content":69},"bullet_list",[70,77,83,89],{"type":71,"content":72},"list_item",[73],{"type":16,"content":74},[75],{"text":76,"type":20},"The last green build had 18.",{"type":71,"content":78},[79],{"type":16,"content":80},[81],{"text":82,"type":20},"After a couple of ordinary installs on my Mac, 16.",{"type":71,"content":84},[85],{"type":16,"content":86},[87],{"text":88,"type":20},"After running the fix everybody recommends, still 16.",{"type":71,"content":90},[91],{"type":16,"content":92},[93],{"text":94,"type":20},"After deleting the lockfile and regenerating it from scratch, 1.",{"type":16,"content":96},[97],{"text":98,"type":20},"Deleting the lockfile is the worst available option, not the best one. It resolves cleanly for the machine you are sitting at and discards everything else.",{"type":38,"attrs":100,"content":101},{"level":40},[102],{"text":103,"type":20},"The fix that everyone documents does not work",{"type":16,"content":105},[106,108,112],{"text":107,"type":20},"The standard advice is ",{"text":109,"type":20,"marks":110},"npm install --package-lock-only",[111],{"type":34},{"text":113,"type":20},", on the grounds that it resolves from registry metadata rather than from your installed tree. I had this written down in my own notes as the answer. It did not restore anything.",{"type":38,"attrs":115,"content":116},{"level":40},[117],{"text":118,"type":20},"What actually worked",{"type":16,"content":120},[121,123,127,129,133],{"text":122,"type":20},"Generate the lockfile on the platform CI uses. Mount a temporary directory containing only ",{"text":124,"type":20,"marks":125},"package.json",[126],{"type":34},{"text":128,"type":20}," and the last known good lockfile, so your real ",{"text":130,"type":20,"marks":131},"node_modules",[132],{"type":34},{"text":134,"type":20}," is not touched:",{"type":22,"content":136},[137],{"text":138,"type":20},"T=$(mktemp -d); cp package.json \"$T\"\u002F\ngit show \u003Clast-green-commit>:package-lock.json > \"$T\"\u002Fpackage-lock.json\ndocker run --rm --platform linux\u002Famd64 -v \"$T\":\u002Fwork -w \u002Fwork node:24 \\\n  bash -lc 'npm install --package-lock-only && npm ci --ignore-scripts'\ncp \"$T\"\u002Fpackage-lock.json .",{"type":16,"content":140},[141],{"text":142,"type":20},"That restored the full set. Then verify on your own machine too, because a lockfile that fixes CI can just as easily break the laptop, and only running both proves it.",{"type":16,"content":144},[145,147,151,153,156],{"text":146,"type":20},"One warning worth the paragraph. Do not mount your project directory into that container. ",{"text":148,"type":20,"marks":149},"npm ci",[150],{"type":34},{"text":152,"type":20}," wipes and rebuilds ",{"text":130,"type":20,"marks":154},[155],{"type":34},{"text":157,"type":20},", and it will happily fill your working copy with Linux binaries. I did that, and spent the next ten minutes wondering why nothing would build locally.","CI died on a package nobody had heard of. npm had quietly removed every platform variant except the one my Mac needed, and the fix everybody documents makes it worse.","blog_post","2026-08-12 09:00","npm-pruned-your-lockfile-to-your-laptop","blog\u002Fnpm-pruned-your-lockfile-to-your-laptop",null,-90,[],false,215738016545577,"7d903312-de86-4759-9ae9-fcb14c6c50e9","default",[]]