Remove the redundant, always-failing npm run prisma:migrate step in start.js #31

Open
opened 2026-06-19 16:32:56 +00:00 by padreug · 1 comment
Owner

Summary

On every boot the daemon logs a full ENOENT error + Error: Command failed: npm run prisma:migrate stack trace, then continues. It's harmless today but it's pure noise, it masks real startup errors, and the catch-and-continue is a latent footgun.

Symptom (cfaun / ariege-io, nsecbunkerd 0.10.5, every boot)

Running migrations
> npx prisma migrate deploy
npm error code ENOENT
npm error syscall spawn sh
npm error path /nix/store/…-nsecbunkerd-0.10.5/share/nsecbunkerd
npm error enoent spawn sh ENOENT
Error: Command failed: npm run prisma:migrate
    at execSync (node:child_process:963:15)
    at Object.<anonymous> (…/share/nsecbunkerd/scripts/start.js:22:3)
  status: 254 …

…then the daemon proceeds normally (✅ nsecBunker ready to serve requests.).

Cause

scripts/start.js:22 does execSync('npm run prisma:migrate'). In the nix-packaged runtime there is no npm/sh on the service PATH, so the spawn ENOENTs before prisma migrate deploy ever runs. The surrounding code swallows the failure and continues.

Why it's currently harmless (verified)

Migrations are actually applied at deploy time, not by the daemon. On cfaun, _prisma_migrations contains all 24 migrations including the latest:

20260619125847_live_grant_lifecycle_schema | finished

So the in-daemon migrate is redundant — the deploy's migrate deploy already did the work. (This was confirmed while deploying #27: schema correct, data intact, migration recorded — the only thing wrong was this scary log line.)

Why it's still worth fixing

  1. Noise / false alarm. A status: 254 + stack trace on every boot trains operators to ignore startup errors, and buried a real investigation during the #27 rollout (we initially mis-read it as "migration didn't apply").
  2. Latent footgun. Because the failure is caught and ignored, if a host ever relied on the daemon (not the deploy) to migrate, a schema change would silently not apply and the daemon would start against a stale schema — exactly the mismatch we feared during #27.

Proposed fix (pick one)

  • Simplest: remove the execSync('npm run prisma:migrate') step from start.js and rely on deploy-time migrate deploy (current reality on the nix hosts).
  • Or invoke the prisma engine directly with the NixOS prisma env (no shelling out to npm), so it works in-process.
  • Or at minimum, if the step is kept, make it non-fatal-quiet when the tooling isn't present, and loud-fatal when migrate runs but reports pending/failed migrations — i.e. never the current "errors scarily, then ignores it regardless" behavior.

Cross-refs

  • #27 — where this surfaced (the live-grant-lifecycle migration).
## Summary On every boot the daemon logs a full `ENOENT` error + `Error: Command failed: npm run prisma:migrate` stack trace, then continues. It's harmless **today** but it's pure noise, it masks real startup errors, and the catch-and-continue is a latent footgun. ## Symptom (cfaun / ariege-io, nsecbunkerd 0.10.5, every boot) ``` Running migrations > npx prisma migrate deploy npm error code ENOENT npm error syscall spawn sh npm error path /nix/store/…-nsecbunkerd-0.10.5/share/nsecbunkerd npm error enoent spawn sh ENOENT Error: Command failed: npm run prisma:migrate at execSync (node:child_process:963:15) at Object.<anonymous> (…/share/nsecbunkerd/scripts/start.js:22:3) status: 254 … ``` …then the daemon proceeds normally (`✅ nsecBunker ready to serve requests.`). ## Cause `scripts/start.js:22` does `execSync('npm run prisma:migrate')`. In the nix-packaged runtime there is no `npm`/`sh` on the service `PATH`, so the spawn `ENOENT`s before `prisma migrate deploy` ever runs. The surrounding code swallows the failure and continues. ## Why it's currently harmless (verified) Migrations are actually applied at **deploy** time, not by the daemon. On cfaun, `_prisma_migrations` contains all 24 migrations including the latest: ``` 20260619125847_live_grant_lifecycle_schema | finished ``` So the in-daemon `migrate` is **redundant** — the deploy's `migrate deploy` already did the work. (This was confirmed while deploying #27: schema correct, data intact, migration recorded — the only thing wrong was this scary log line.) ## Why it's still worth fixing 1. **Noise / false alarm.** A `status: 254` + stack trace on every boot trains operators to ignore startup errors, and buried a *real* investigation during the #27 rollout (we initially mis-read it as "migration didn't apply"). 2. **Latent footgun.** Because the failure is caught and ignored, if a host ever relied on the daemon (not the deploy) to migrate, a schema change would silently not apply and the daemon would start against a stale schema — exactly the mismatch we feared during #27. ## Proposed fix (pick one) - **Simplest: remove the `execSync('npm run prisma:migrate')` step from `start.js`** and rely on deploy-time `migrate deploy` (current reality on the nix hosts). - **Or** invoke the prisma engine directly with the NixOS prisma env (no shelling out to `npm`), so it works in-process. - **Or** at minimum, if the step is kept, make it **non-fatal-quiet** when the tooling isn't present, and **loud-fatal** when migrate runs but reports pending/failed migrations — i.e. never the current "errors scarily, then ignores it regardless" behavior. ## Cross-refs - #27 — where this surfaced (the live-grant-lifecycle migration).
Author
Owner

⚠️ Caveat before anyone implements this as a straight removal: start.js's migrate step is load-bearing on docker, only dead on nix.

  • Docker: Dockerfile sets ENTRYPOINT ["node", "./scripts/start.js"] with the explicit comment "Run via scripts/start.js so prisma migrate deploy applies pending migrations", and docker-compose.yml also wires a dedicated migrate command. devDeps (incl. the prisma CLI) are kept in the image, so npm run prisma:migrate works there — it's the migration path.
  • Nix: the deploy runs prisma migrate deploy itself, and start.js's execSync('npm run prisma:migrate') fails in the read-only nix store (no npm on PATH) → caught + logged. Redundant + noisy there, hence the "always-failing" framing — but that's nix-specific.

So a blanket removal breaks docker migrations. Options:

  1. Make it robust on both — invoke the prisma CLI directly (resolve node_modules/.bin/prisma migrate deploy instead of npm run), so it works on nix too and stops being a no-op rather than being deleted.
  2. Scope to nix — only the nix path is redundant; leave the daemon step for docker, just silence the nix-side failure.
  3. Tie to the docker-support decision — if docker is being deprecated anyway (cf. #6, arm64-only image), removal becomes fine; otherwise not.

Leaning (1) — it's the smallest change that makes the step correct everywhere instead of dead-on-one-platform. Flagging so this doesn't ship as a removal that silently breaks the docker path.

⚠️ Caveat before anyone implements this as a straight removal: **`start.js`'s migrate step is load-bearing on docker**, only dead on nix. - **Docker**: `Dockerfile` sets `ENTRYPOINT ["node", "./scripts/start.js"]` with the explicit comment *"Run via scripts/start.js so `prisma migrate deploy` applies pending migrations"*, and `docker-compose.yml` also wires a dedicated `migrate` command. devDeps (incl. the prisma CLI) are kept in the image, so `npm run prisma:migrate` **works** there — it's the migration path. - **Nix**: the deploy runs `prisma migrate deploy` itself, and `start.js`'s `execSync('npm run prisma:migrate')` fails in the read-only nix store (no `npm` on PATH) → caught + logged. Redundant + noisy there, hence the "always-failing" framing — but that's nix-specific. So a blanket removal breaks docker migrations. Options: 1. **Make it robust on both** — invoke the prisma CLI directly (resolve `node_modules/.bin/prisma migrate deploy` instead of `npm run`), so it works on nix too and stops being a no-op rather than being deleted. 2. **Scope to nix** — only the nix path is redundant; leave the daemon step for docker, just silence the nix-side failure. 3. **Tie to the docker-support decision** — if docker is being deprecated anyway (cf. #6, arm64-only image), removal becomes fine; otherwise not. Leaning (1) — it's the smallest change that makes the step correct everywhere instead of dead-on-one-platform. Flagging so this doesn't ship as a removal that silently breaks the docker path.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/nsecbunkerd#31
No description provided.