diff --git a/src/packages/dumbo/src/core/connections/transaction.ts b/src/packages/dumbo/src/core/connections/transaction.ts index 2bcbef68..548911ad 100644 --- a/src/packages/dumbo/src/core/connections/transaction.ts +++ b/src/packages/dumbo/src/core/connections/transaction.ts @@ -80,7 +80,11 @@ export const executeInTransaction = async < return result; } catch (e) { - await transaction.rollback(); + try { + await transaction.rollback(); + } catch { + // rollback failed — preserve the original error + } throw e; } }; diff --git a/src/packages/dumbo/src/storage/sqlite/sqlite3/transactions/transactionErrorSuppression.int.spec.ts b/src/packages/dumbo/src/storage/sqlite/sqlite3/transactions/transactionErrorSuppression.int.spec.ts new file mode 100644 index 00000000..dc9d4c35 --- /dev/null +++ b/src/packages/dumbo/src/storage/sqlite/sqlite3/transactions/transactionErrorSuppression.int.spec.ts @@ -0,0 +1,41 @@ +import assert from 'assert'; +import { describe, it } from 'vitest'; +import { sqlite3Connection } from '..'; +import { JSONSerializer, SQL } from '../../../../core'; +import { InMemorySQLiteDatabase } from '../../core'; + +describe('withTransaction error preservation', () => { + it('should surface the original callback error, not the rollback error', async () => { + const connection = sqlite3Connection({ + fileName: InMemorySQLiteDatabase, + serializer: JSONSerializer, + }); + + try { + try { + await connection.withTransaction(async (tx) => { + await tx.execute.command( + SQL`CREATE TABLE IF NOT EXISTS test_error (id INTEGER, value TEXT)`, + ); + + // Close the underlying database to cause rollback to fail + await connection.close(); + + throw new Error('original callback error'); + }); + assert.fail('should have thrown'); + } catch (err) { + assert.strictEqual( + (err instanceof Error ? err.message : String(err)), + 'original callback error', + ); + } + } finally { + try { + await connection.close(); + } catch { + // connection may already be closed + } + } + }); +});