Failed COMMIT leaves transaction open and breaks subsequent transactions
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 68/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- react-native, sqlite, typescript
- Domain
- databases
Research direction
Read packages/react-native-nitro-sqlite/src/operations/transaction.ts, especially the commit and rollback paths at the cited lines, and compare manual commit with automatic commit behavior. Add the regression coverage in a Harness test using the deferred foreign-key reproducer; done means the original commit error is preserved, the inserted row is absent, rollback errors remain visible if applicable, and a second transaction succeeds on the same connection.
Written by the indexing model from the issue text.
Description
Summary
When COMMIT fails, transaction() marks the transaction as finished before executing the SQL. The catch block then skips rollback, leaving SQLite inside the failed transaction. A later transaction on the same connection fails with cannot start a transaction within a transaction.
Verified on main at ad8b835 (v9.7.0).
Code evidence
transaction.ts#L44-L52 sets isFinished = true before execute(dbName, 'COMMIT').
If that execute throws, control reaches transaction.ts#L81-L90. The rollback guard is if (!isFinished), so rollback is skipped.
SQLite can legitimately fail COMMIT, including for deferred foreign-key constraints, I/O errors, and busy/locking conditions. A failed commit can leave the transaction active.
Smallest reliable reproducer
const db = open({ name: 'commit-failure.sqlite' })
db.execute('PRAGMA foreign_keys = ON')
db.execute('CREATE TABLE parent (id INTEGER PRIMARY KEY)')
db.execute(`
CREATE TABLE child (
parent_id INTEGER
REFERENCES parent(id)
DEFERRABLE INITIALLY DEFERRED
)
`)
await expect(
db.transaction(async (tx) => {
tx.execute('INSERT INTO child(parent_id) VALUES (?)', [404])
}),
).rejects.toThrow(/FOREIGN KEY constraint failed/)
// Expected: starts cleanly after the failed transaction was rolled back.
// Observed: "cannot start a transaction within a transaction".
await db.transaction(async (tx) => {
tx.execute('SELECT 1')
})
The underlying SQLite sequence is deterministic:
PRAGMA foreign_keys = ON;
CREATE TABLE parent(id INTEGER PRIMARY KEY);
CREATE TABLE child(parent_id INTEGER REFERENCES parent(id) DEFERRABLE INITIALLY DEFERRED);
BEGIN;
INSERT INTO child(parent_id) VALUES (404);
COMMIT; -- FOREIGN KEY constraint failed
BEGIN; -- cannot start a transaction within a transaction
Impact
One commit-time failure poisons the connection for later writes. Callers receive the original commit error, but the library's transaction abstraction no longer restores its documented all-or-nothing/usable-connection invariant.
Acceptance criteria
- Do not transition to a committed/finalized state until
COMMITsucceeds. - On commit failure, attempt rollback while the transaction remains active.
- Preserve the original commit failure; if rollback also fails, expose both errors without replacing the primary cause.
- Model transaction state explicitly enough to distinguish active, committed, rolled back, and failed-finalization states.
- Keep manual
commit()and automatic commit behavior consistent.
Regression-test target
A Harness test using a deferred foreign-key violation that asserts:
- The first transaction rejects with the commit-time constraint error.
- Its inserted row is absent.
- A second transaction on the same connection begins and commits successfully.
- Dominant language
- C
- Stars
- 579
- Forks
- 53
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 67
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from margelo/react-native-nitro-sqlite
-
area:api area:runtime enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
margelo/react-native-nitro-sqlite#402 ·
Maintainers usually reply within 1 day
-
area:api area:runtime enhancement
Difficulty 5/5 Over a week Newbie friendliness 28/100
margelo/react-native-nitro-sqlite#401 ·
Maintainers usually reply within 1 day
-
area:api area:runtime enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
margelo/react-native-nitro-sqlite#400 ·
Maintainers usually reply within 1 day
-
area:api area:runtime enhancement
Difficulty 5/5 Over a week Newbie friendliness 28/100
margelo/react-native-nitro-sqlite#399 ·
Maintainers usually reply within 1 day
-
area:api area:runtime enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
margelo/react-native-nitro-sqlite#398 ·
Maintainers usually reply within 1 day
All issues in margelo/react-native-nitro-sqlite
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
EchoTools/nevr-runtime#117 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
DarkFlippers/qUnleashed#240 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
HarbourMasters/Shipwright#7320 ·
Maintainers usually reply within 1 day
-
area/documentation status/awaiting-triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
yugabyte/yugabyte-db#34660 ·
Maintainers usually reply within 1 day