TryGhost / node-sqlite3

SQLite3 bindings for Node.js
BSD 3-Clause "New" or "Revised" License
6.19k stars 812 forks source link

TypeError: Cannot read properties of null (reading 'close') #1795

Open miniBill opened 1 month ago

miniBill commented 1 month ago

Issue Summary

When I call

db.close((err) => {
    if (err) {
        reject(err);
    } else {
        resolve(db);
    }
});

I get the error:

node:events:513
  const handler = events[type];
                        ^

TypeError: Cannot read properties of null (reading 'close')
    at Database.emit (node:events:513:25)

The value of db is

image

Steps to Reproduce

// see below for how I defined the sqlite_* functions
// they're basically promisified versions of open/serialize/close

const db = await sqlite_open("../feeds.sqlite");
await sqlite_serialize({db: db, statements: [...]});
await sqlite_close(db);

export async function sqlite_open(filename: string): Promise<sqlite3.Database> {
    return await new Promise<sqlite3.Database>((resolve, reject) => {
        const db = new sqlite3.Database(filename, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve(db);
            }
        });
    });
}

export async function sqlite_close(db: sqlite3.Database): Promise<{}> {
    return await new Promise<{}>((resolve, reject) => {
        debugger;
        db.close((err) => {
            if (err) {
                reject(err);
            } else {
                resolve({});
            }
        });
    });
}

export async function sqlite_serialize({
    db,
    statements,
}: {
    db: sqlite3.Database;
    statements: string[];
}): Promise<{}> {
    const promises: Promise<{}>[] = [];
    db.serialize(() => {
        for (const statement of statements) {
            console.info(statement);
            promises.push(
                new Promise<{}>((resolve, reject) => {
                    db.run(statement, [], (err) => {
                        if (err) {
                            reject(err);
                        } else {
                            resolve({});
                        }
                    });
                })
            );
        }
    });
    await Promise.all(promises);
    return {};
}

Version

5.1.7

Node.js Version

18.20.4

How did you install the library?

yarn add sqlite3

miniBill commented 1 month ago

I get the same error if I try to run this with node 20.15.1

miniBill commented 1 month ago

If I replace the close + callback with

db.once("error", reject);
db.once("close", resolve);
db.close();

I get a different error when running the first line:

image

Might it be because _events is null?

miniBill commented 1 month ago

The issue seems to go away if I instead of giving a callback to open I use db.once to wait for the database to be correctly opened (in which case, _events is not null)

miniBill commented 1 month ago

Yeah, it seems to be an issue with giving a callback to open. If I replace the open code to:

const db = new sqlite3.Database(filename);
return await new Promise<sqlite3.Database>((resolve, reject) => {
    const error = function () {
        db.off("open", success);
        db.off("error", error);
        reject();
    };
    const success = function () {
        db.off("open", success);
        db.off("error", error);
        resolve(db);
    };
    db.on("error", error);
    db.on("open", success);
});

then everything works correctly, including using callbacks for the other operations.