Skip to content

Commit be51c7f

Browse files
committed
fix(dumbo): preserve original error when transaction rollback fails
When a withTransaction callback throws, executeInTransaction attempts a rollback. If rollback also fails (e.g. connection closed, SQLITE_MISUSE), the rollback error replaces the original callback error — making debugging impossible. Wrap rollback in its own try/catch so the original error is always preserved and re-thrown to the caller.
1 parent 45b5b72 commit be51c7f

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

src/packages/dumbo/src/core/connections/transaction.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ export const executeInTransaction = async <
8080

8181
return result;
8282
} catch (e) {
83-
await transaction.rollback();
83+
try {
84+
await transaction.rollback();
85+
} catch {
86+
// rollback failed — preserve the original error
87+
}
8488
throw e;
8589
}
8690
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import assert from 'assert';
2+
import { describe, it } from 'vitest';
3+
import { sqlite3Connection } from '..';
4+
import { JSONSerializer, SQL } from '../../../../core';
5+
import { InMemorySQLiteDatabase } from '../../core';
6+
7+
describe('withTransaction error preservation', () => {
8+
it('should surface the original callback error, not the rollback error', async () => {
9+
const connection = sqlite3Connection({
10+
fileName: InMemorySQLiteDatabase,
11+
serializer: JSONSerializer,
12+
});
13+
14+
try {
15+
try {
16+
await connection.withTransaction(async (tx) => {
17+
await tx.execute.command(
18+
SQL`CREATE TABLE IF NOT EXISTS test_error (id INTEGER, value TEXT)`,
19+
);
20+
21+
// Close the underlying database to cause rollback to fail
22+
await connection.close();
23+
24+
throw new Error('original callback error');
25+
});
26+
assert.fail('should have thrown');
27+
} catch (err) {
28+
assert.strictEqual(
29+
(err instanceof Error ? err.message : String(err)),
30+
'original callback error',
31+
);
32+
}
33+
} finally {
34+
try {
35+
await connection.close();
36+
} catch {
37+
// connection may already be closed
38+
}
39+
}
40+
});
41+
});

0 commit comments

Comments
 (0)