react-nativesqliteexpo

Why I receive Expo SQLite: Persistent 'Unhandled promise rejection' with NullPointerException in NativeDatabase.execAsync?


I'm developing a React Native application using Expo (SDK 51) and SQLite. I've implemented local database operations for managing orders, but I'm encountering a persistent warning about an unhandled promise rejection. Here are the details:

The Issue

On each app refresh, I receive the following warning:

Possible unhandled promise rejection (id: X):
Error: Call to function 'NativeDatabase.execAsync' has been rejected.
-> Caused by: java.lang.NullPointerException: java.lang.NullPointerException

The warning appears continuously, and the id increments each time (e.g., id: 1, id: 2, id: 3, etc.).

My Code

Here's the relevant code for my database operations:

import * as SQLite from 'expo-sqlite';
import { useEffect } from 'react';

async function createLocalDatabase() {
  const db = await SQLite.openDatabaseAsync("orders.db");
  await db.execAsync(`
    PRAGMA journal_mode = WAL;
    CREATE TABLE IF NOT EXISTS order_header (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    firm TEXT NOT NULL,
    shop TEXT NOT NULL,
    delivery_date DATE NOT NULL,
    notice TEXT,
    counterparty_id INTEGER
    );

    CREATE TABLE IF NOT EXISTS order_items (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_name TEXT NOT NULL,
    sale_unit TEXT NOT NULL,
    quantity REAL NOT NULL,
    order_header_id INTEGER NOT NULL,
    FOREIGN KEY (order_header_id) REFERENCES order_header (id)
    );
  `);
}

useEffect(() => {
  createLocalDatabase();
}, []);

async function insertOrderHeader(db, firm, shop, deliveryDate, notice, counterpartyId) {
  const statement = await db.prepareAsync(
    "INSERT INTO order_header (firm, shop, delivery_date, notice, counterparty_id) VALUES (?, ?, ?, ?, ?)"
  );

  try {
    const result = await statement.executeAsync([
      firm,
      shop,
      deliveryDate,
      notice,
      counterpartyId,
    ]);
    return result.lastInsertRowId;
  } finally {
    await statement.finalizeAsync();
  }
}

async function insertOrderItem(
  db,
  orderHeaderId,
  itemName,
  saleUnit,
  quantity
) {
  const statement = await db.prepareAsync(
    "INSERT INTO order_items (order_header_id, item_name, sale_unit, quantity) VALUES (?, ?, ?, ?)"
  );

  try {
    await statement.executeAsync([
      orderHeaderId,
      itemName,
      saleUnit,
      quantity,
    ]);
  } finally {
    await statement.finalizeAsync();
  }
}

async function insertOrderWithItems(
  firm,
  shop,
  deliveryDate,
  notice,
  orderItems
) {
  const db = await SQLite.openDatabaseAsync("orders.db");

  try {
    await db.execAsync("BEGIN TRANSACTION");

    const orderHeaderId = await insertOrderHeader(
      db,
      firm,
      shop,
      deliveryDate,
      notice
    );

    for (const item of orderItems) {
      await insertOrderItem(
        db,
        orderHeaderId,
        item.name,
        item.saleUnit,
        item.quantity
      );
    }

    await db.execAsync("COMMIT");

    console.log(
      "Order with items inserted successfully. Order ID:",
      orderHeaderId
    );

    return orderHeaderId;
  } catch (error) {
    await db.execAsync("ROLLBACK");
    console.error("Error inserting order with items:", error);
    throw error;
  }
}

What I've Tried

  1. I've ensured that all SQLite operations are wrapped in try/catch blocks.
  2. I'm using await for all asynchronous SQLite operations.
  3. I've implemented transaction handling for inserting orders with items.

Questions

  1. Why am I receiving this persistent unhandled promise rejection warning?
  2. Why does the warning's ID increment on each app refresh?
  3. How can I properly handle or resolve this issue?
  4. Is there a problem with how I'm initializing or using the database?

Any insights or suggestions would be greatly appreciated. Thank you!

Environment

Expo ver: ~51.0.18

expo-sqlite version: ~14.0.5

React Native version: 0.74.3

Device: Motorola E20


Solution

  • I had the same issue with the same phone. It seems to be an error from Motorola and how it handles NativeDatabase, because in other devices such as TCL or Samsung this issue doesn't reproduce.

    The fix is use a new connection each time you open the database

       const db = await SQLite.openDatabaseAsync(databaseFile, {
      useNewConnection: true
    });