Node.js "--unhandled-rejections=strict" Changed CI Exit Behavior
Node’s --unhandled-rejections flag controls what happens when a promise rejects unhandled. A pipeline that set warn (or ran old Node) tolerated rejections; switching to strict/throw makes them fatal.
What this error means
After a Node upgrade or a changed NODE_OPTIONS, a job that previously only logged a warning now exits non-zero on the same unhandled rejection. Nothing in your code changed - only the rejection mode did.
# old behaviour (warn): job stayed green
(node:1) UnhandledPromiseRejectionWarning: TypeError: ...
# new behaviour (strict/throw): job fails
node:internal/process/promises:288 ... code: 'ERR_UNHANDLED_REJECTION'Common causes
The default mode changed across Node versions
Older Node warned on unhandled rejections; Node 15+ defaults to throw. A runner image bump to a newer Node flips the same code from warning to fatal.
NODE_OPTIONS sets a stricter mode
A pipeline that adds --unhandled-rejections=strict raises severity so even rejections inside the first tick of loading abort the process.
How to fix it
Fix the rejection rather than the mode
The correct response to a newly-fatal rejection is to handle it. Lowering the mode just hides a real bug.
- Find the unhandled rejection in the stack or the prior warning.
- Await it in
try/catchor attach.catch(). - Keep
throw/strictso future rejections stay visible.
Set the mode deliberately if you must
If you genuinely need to soften behaviour during a migration, set the mode explicitly so it is documented - but treat it as temporary.
# strict (recommended long-term)
node --unhandled-rejections=strict app.mjs
# or via env
NODE_OPTIONS=--unhandled-rejections=strictHow to prevent it
- Pin a Node version in CI so the default rejection mode does not change under you.
- Lint for floating promises so rejections never go unhandled in the first place.
- Keep
--unhandled-rejections=strictand treat any rejection as a bug.