node-sqlite3 icon indicating copy to clipboard operation
node-sqlite3 copied to clipboard

Node-API crash with unhandled promise after SQLITE_CANTOPEN

Open dnicolson opened this issue 1 year ago • 3 comments

Issue Summary

The following crash occurs when a promise doesn't resolve and the SQLite file cannot be opened:

  • Version 4.2.0 is not affected
  • The crash occurs with the path xxx/x.db but not x.db
FATAL ERROR: Error::ThrowAsJavaScriptException napi_throw
 1: 0x104cb21dc node::Abort() [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
 2: 0x104cb22d4 node::OOMErrorHandler(char const*, bool) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
 3: 0x104cb21f4 node::OnFatalError(char const*, char const*) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
 4: 0x104c82d34 napi_open_callback_scope [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
 5: 0x113bece9c Napi::Error::Error(napi_env__*, napi_value__*) [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
 6: 0x113beaa6c Napi::Error::ThrowAsJavaScriptException() const [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
 7: 0x113bec744 Napi::Reference<Napi::Object>::Unref() [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
 8: 0x113c050f4 node_sqlite3::Statement::Baton::~Baton() [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
 9: 0x113c08028 node_sqlite3::Statement::EachBaton::~EachBaton() [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
10: 0x113bfc830 node_sqlite3::Statement::CleanQueue() [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
11: 0x113c08228 node_sqlite3::Statement::~Statement() [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
12: 0x113c04c70 node_sqlite3::Statement::~Statement() [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
13: 0x113c06da0 Napi::ObjectWrap<node_sqlite3::Statement>::FinalizeCallback(napi_env__*, void*, void*) [<project>/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node]
14: 0x104c8570c void napi_env__::CallIntoModule<void node_napi_env__::CallFinalizer<true>(void (*)(napi_env__*, void*, void*), void*, void*)::'lambda'(napi_env__*)&, void node_napi_env__::CallbackIntoModule<true, void node_napi_env__::CallFinalizer<true>(void (*)(napi_env__*, void*, void*), void*, void*)::'lambda'(napi_env__*)>(void node_napi_env__::CallFinalizer<true>(void (*)(napi_env__*, void*, void*), void*, void*)::'lambda'(napi_env__*)&&)::'lambda'(napi_env__*, v8::Local<v8::Value>)>(void node_napi_env__::CallFinalizer<true>(void (*)(napi_env__*, void*, void*), void*, void*)::'lambda'(napi_env__*)&, void node_napi_env__::CallFinalizer<true>(void (*)(napi_env__*, void*, void*), void*, void*)::'lambda'(napi_env__*)&&) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
15: 0x104c8245c void node_napi_env__::CallFinalizer<true>(void (*)(napi_env__*, void*, void*), void*, void*) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
16: 0x104c6cbb0 v8impl::Reference::Finalize(bool) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
17: 0x104c82254 node_napi_env__::DeleteMe() [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
18: 0x104c252bc node::CleanupQueue::Drain() [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
19: 0x104c59d8c node::Environment::RunCleanup() [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
20: 0x104c00204 node::FreeEnvironment(node::Environment*) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
21: 0x104ced124 node::NodeMainInstance::Run() [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
22: 0x104c80ec8 node::LoadSnapshotDataAndRun(node::SnapshotData const**, node::InitializationResult const*) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
23: 0x104c81190 node::Start(int, char**) [/Users/dave/.asdf/installs/nodejs/18.11.0/bin/node]
24: 0x180fb3e50 start [/usr/lib/dyld]

Steps to Reproduce

  1. Setup project directory and empty files
mkdir test && cd test
touch index.js
touch index.test.js
  1. Write to index.js and index.test.js:

index.js:

const sqlite3 = require('sqlite3').verbose();

const testFunction = () => {
    const db = new sqlite3.Database('xxx/x.db');
    return new Promise((_resolve, _reject) => {
        db.each('SELECT * FROM Files', (_err, _row) => {});
    });
}

exports.main = async () => {
    await testFunction();
};

index.test.js:

const testFunction = require('./index').main

it('crashes', async () => {
    await testFunction(); 
});
  1. Run the test
npm i sqlite3 jest-cli
npx jest index.test.js

Version

5.1.2

Node.js Version

18.11.0

How did you install the library?

Via npm on macOS 13.0 Beta (22A5373b) arm64

dnicolson avatar Oct 18 '22 19:10 dnicolson

I also ran into this error, however, it is not a bug. You simply need to handle errors that can occur during database opening by adding a callback to your new sqlite3.Database constructor and turning it into a promise, like so:

const mode = sqlite3.OPEN_READWRITE | sqlite3.OPEN_FULLMUTEX; // Or whatever you want
const db = await new Promise((resolve, reject) => {
  const db_ = new sqlite3.Database('xxx/x.db', mode, err => {
    if (err) {
      reject(err);
    } else {
      resolve(db_);
    }
  });
});

daniel-chambers avatar Mar 17 '23 05:03 daniel-chambers

I also handled the error the same way.

The provided example is a contrived example to trigger the issue. It's about the error handling in the C++ code and not the JavaScript code, it shouldn't be possible to raise a Node-API C++ exception from Node.js.

dnicolson avatar Mar 17 '23 05:03 dnicolson

Oh okay, fair enough, I stand corrected 🙂

daniel-chambers avatar Mar 17 '23 05:03 daniel-chambers