Push mendarat, git bersih, semua ref cocok, dan produksi diam-diam tetap menyajikan bundle lama. Itulah yang terlihat pada 29 Agustus 2026, ketika dua push ke branch produksi klien tidak menghasilkan deploy. Setelah ditelusuri, produksi ternyata sudah tidak ter-deploy sejak 21 Agustus, delapan hari. CI build klien di AWS CodeBuild gagal di langkah pnpm install, sebelum satu baris pun dikompilasi.
Log-nya pendek dan tidak menyisakan banyak tafsiran:
Error: ERR_PNPM_IGNORED_BUILDS
Ignored build scripts: esbuild@0.27.7, sharp@0.34.5, unrs-resolver@1.11.1
Command did not exit successfully pnpm install exit status 1Saya pernah menulis tentang build Turbopack yang gagal sementara versi lama tetap tayang, dan sekilas kasus ini terlihat sejenis. Bedanya ada di asal masalahnya. Di sana penyebabnya ada di repo, yaitu paket yang di-import tanpa dideklarasikan. Di sini tidak ada isi repo yang menyebabkannya.
Tidak ada commit di belakangnya
Sumbernya ada di pnpm. Menurut perubahan yang dirilis pnpm sendiri, versi 10 berhenti menjalankan lifecycle script milik dependency kecuali script itu sudah diputuskan, lalu sebuah versi sesudahnya menaikkan peringatan yang tersisa menjadi error keras. Nomor versi yang menaikkannya tidak ada di catatan saya.
Perubahan di hulu seperti itu biasanya baru sampai ke sebuah proyek ketika proyek itu memilih naik versi. Proyek ini naik versi tanpa ada yang memilih. Saat itu package.json tidak punya field packageManager, sehingga image build tinggal memasang pnpm mana pun yang kebetulan paling baru. Pada hari versi itu rilis, build rusak dengan sendirinya, tanpa satu commit pun di belakangnya.
Pelajaran utamanya ada di situ. Selama package manager tidak di-pin, build adalah sasaran bergerak, dan dia akan rusak tepat di hari ketika kamu tidak mengubah apa pun.
Tiga ejaan, cuma satu yang dibaca
Memperbaikinya ternyata tidak sesederhana menyebut paket mana yang boleh menjalankan build script. Keputusan itu bisa ditulis di tiga tempat, dan di pnpm 11 hanya satu yang dibaca:
package.json, field "pnpm"
onlyBuiltDependencies tidak dibaca di pnpm 11: lokasi lama, diabaikan diam-diam
pnpm-workspace.yaml
onlyBuiltDependencies / ignoredBuiltDependencies tidak dibaca di pnpm 11: bentuk daftar lama
pnpm-workspace.yaml
allowBuilds (peta nama paket ke boolean) dibacaDua siklus clean install habis untuk dua ejaan pertama, sampai pnpm sendiri menulis ulang pnpm-workspace.yaml dan menyiapkan kerangka allowBuilds: berisi placeholder set this to true or false. Sejak itu saya pegang satu aturan: kalau ragu kunci mana yang diminta sebuah versi pnpm, jalankan install dan biarkan pnpm yang menulis berkasnya, karena dia akan menyebut kunci yang dia baca.
Berkas yang ternyata sudah berisi keputusan
Ada kesalahan lain yang lebih berbahaya daripada salah ejaan. pnpm-workspace.yaml ternyata sudah ada sebelumnya dan berisi keputusan yang diambil dengan sengaja lebih awal. Riwayat git-nya menunjukkan isi itu masuk lewat commit berjudul "Initial commit from Create Next App" tanggal 20 April 2026:
ignoredBuiltDependencies:
- sharp
- unrs-resolverBerkas itu saya timpa tanpa dibaca dulu, sehingga sharp dan unrs-resolver terbalik jadi true. Perkiraan saya, pembalikan itu hanya akan menukar satu kegagalan dengan kegagalan lain, karena CodeBuild berjalan di Linux dan build script sharp butuh libvips, sementara di Mac lolos. Itu dugaan, bukan hasil uji, karena nilai itu tidak pernah ikut terkirim.
Kesalahannya tertangkap hanya karena git status menandai berkas itu M, bukan sebagai berkas baru. Tanpa satu huruf itu, saya tidak akan sadar. Aturannya sejak itu: baca berkas konfigurasi sebelum menulisnya, dan anggap M di diffstat sebagai tanda bahwa kamu sedang mengubah keputusan orang lain.
Nilai yang dikirim: semuanya false
Yang akhirnya dikirim mempertahankan maksud awal itu, bukan membaliknya. Alasannya, ketiga paket mengirim paket platform prebuilt, yaitu @img/sharp-*, @esbuild/*, dan @unrs/*, yang hanya ditautkan oleh install script. Blok yang masuk ke pnpm-workspace.yaml:
allowBuilds:
esbuild: false
sharp: false
unrs-resolver: falsePesan error tadi menyebut tiga paket, tapi posisinya tidak sama. Paket esbuild memang belum pernah diputuskan ke arah mana pun. Paket sharp dan unrs-resolver sebenarnya sudah diputuskan, hanya saja dalam bentuk daftar lama. Komentar yang saya tinggalkan di berkas itu menjelaskan rantainya: pnpm terkini membaca allowBuilds, bentuk daftar lama tidak dibaca, sehingga isi lama berkas berhenti terhitung sebagai keputusan dan ketiga paket dilaporkan belum diputuskan.
Komentar yang sama memuat peringatan. Membalik salah satu nilai ke true berarti mengizinkan paket itu mengeksekusi kode saat install di mesin build, jadi itu harus keputusan sungguhan yang diverifikasi ulang, bukan refleks ketika ada yang terlihat hilang.
Perbaikannya masuk sebagai commit berjudul fix(build): state the install-script decision in the key current pnpm reads, lalu sampai ke main lewat merge tanggal 29 Agustus yang hanya mengubah satu berkas, pnpm-workspace.yaml, dengan 30 baris tambah dan 3 baris hapus.
Exit code install yang terlewat
Satu kesalahan lagi tidak ada hubungannya dengan kunci mana pun. Sebelumnya di hari yang sama, saya sempat melaporkan "265 tests pass", padahal pnpm install sendiri keluar dengan kode 1. node_modules yang tersisa masih cukup terisi sehingga tsc dan vitest tetap bisa jalan. Hasil test itu nyata, tapi tidak mengatakan apa pun tentang install yang barusan gagal. Luput membaca satu exit code itu menghabiskan lima jam.
Verifikasi yang benar-benar dihitung dimulai dari nol: hapus node_modules, lalu install, tsc, test, dan next build penuh, semuanya exit 0. Pesan commit perbaikannya mencatat angka yang sama, 265 test, kali ini dari node_modules yang sudah dihapus. Jumlah test-nya tidak berubah. Yang berubah hanya exit code install di depannya.
# disederhanakan: baca exit code install dulu, hasil sesudahnya baru berarti kalau ini 0
rm -rf node_modules
pnpm install
echo "exit install: $?"Aturannya: periksa exit code install, bukan hanya perintah yang jalan sesudahnya.
Yang terbukti dan yang belum
Ada satu hal yang perlu saya pisahkan. Verifikasi di atas adalah install bersih, bukan log CodeBuild, dan catatan saya tidak memuat kalimat yang menyatakan build di CodeBuild kembali hijau sesudah perbaikan ini. Jejak terdekatnya tidak langsung. Menurut catatan saya, commit yang sedang tayang dibuktikan lewat keberadaan rute: sebuah rute API yang membalas 200 menempatkan satu merge bertanggal 31 Agustus di produksi, dan sebuah rute POST yang membalas 204 menempatkan merge lain di hari yang sama. Kedua merge itu sudah membawa blok allowBuilds dengan tiga nilai false.
Untuk akar masalahnya, package manager yang tidak di-pin, baris packageManager baru ditambahkan ke package.json pada 31 Agustus, sesudah dua merge itu, lewat commit berjudul build: pin pnpm to 11.21.0 so the build stops being a moving target. Pin itu punya ceritanya sendiri, dan itu bahan tulisan terpisah.
Build yang rusak di hari tanpa perubahan
Kasus ini meninggalkan empat aturan, dan hanya satu yang khusus untuk pnpm:
- Pin versi package manager. Tanpa pin, build adalah sasaran bergerak yang akan rusak di hari ketika tidak ada yang mengubah apa pun.
- Kalau ragu kunci mana yang diminta sebuah versi pnpm, jalankan install dan biarkan pnpm yang menulis berkasnya.
- Baca berkas konfigurasi sebelum menulisnya.
Mdigit statusberarti kamu sedang mengubah keputusan orang lain. - Periksa exit code install sebelum mempercayai test yang jalan sesudahnya.