Description
To reproduce: Run this script. It initializes sql.js, waits one second, then creates a rejected promise. The promise is outside of any code related to sql.js. The only relationship is that it's running in the same process.
const initSqlJs = require("sql.js")
initSqlJs().then(() => {
})
// Wait long enough that sql.js is initialized. But this doesn't directly
// `then` on the initSqlJs promise. That makes it clear that rejected promises
// totally outside of the initSqlJs promise still cause aborts.
setTimeout(() => {
Promise.reject("failure")
setTimeout(() => { console.log("process is still alive") }, 1000)
}, 1000)
Expected result: A warning about the rejected promise should be printed by Node, followed by the "process is still alive" message. This is what happens if you comment out the sql.js-related lines:
$ node test.js
(node:44462) UnhandledPromiseRejectionWarning: failure
(node:44462) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:44462) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
process is still alive
Actual result: sql.js sees the unhandled rejection in a promise that should be outside of sql.js' control. It aborts the entire Node process:
$ node test.js
failure
failure
/Users/grb/proj/ep/node_modules/sql.js/dist/sql-wasm.js:107
function D(a){if(e.onAbort)e.onAbort(a);Ha(a);E(a);Va=!0;throw new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");}function pb(a){var b=qb;return String.prototype.startsWith?b.startsWith(a):0===b.indexOf(a)}function rb(){return pb("data:application/octet-stream;base64,")}var qb="sql-wasm.wasm";if(!rb()){var sb=qb;qb=e.locateFile?e.locateFile(sb,z):z+sb}
^
RuntimeError: abort(failure). Build with -s ASSERTIONS=1 for more info.
at process.D (/Users/grb/proj/ep/node_modules/sql.js/dist/sql-wasm.js:107:64)
at process.emit (events.js:315:20)
at processPromiseRejections (internal/process/promises.js:209:33)
at processTicksAndRejections (internal/process/task_queues.js:98:32)
Discussion: Our application runs sql.js both in the browser and in Node. In the browser, we use it for interactive lessons on SQL. In Node, we use it for automated testing of the lessons.
Some of our code examples in other courses (not the SQL course) intentionally "leak" rejected promises. That test process also loads sql.js during boot, even though it's not in use at the time when rejections are leaked. Merely loading sql.js causes it to intercept the unhandled rejection and abort the Node process.
(The situation seems to be slightly weirder than that in practice. Sometimes unhandled rejections go by without any problems. I haven't been able to detect a pattern. But fortunately the repro code above seems perfectly reliable.)
Versions: We're currently running 1.1.0, but I also upgraded to 1.3.0 (current) and it happened there as well.