Repository navigation
fix(kernel-store): ask SQLite whether a transaction is open - #1089
Merged
Merged
Conversation
The wasm driver tracked the transaction itself in `_inTx`. SQLite ends a transaction on its own after SQLITE_FULL, SQLITE_IOERR or SQLITE_BUSY, and the flag then latched: the next savepoint skipped its `BEGIN`, and releasing it threw "cannot commit - no transaction is active". `inTransaction` now reads `sqlite3_get_autocommit`, which the wasm build binds but does not type — hence the cast, and the check at init that it is there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Without the `pointer` guard, `sqlite3_get_autocommit(undefined)` returns 0 and a closed connection reports a transaction open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…egins Review follow-up. Reading `inTransaction` from SQLite drops the invariant the cached flag carried, that no transaction implies an empty savepoint stack. When SQLite ends a transaction itself, the stale stack makes `commitIfNeeded` defer to a savepoint owner that no longer exists, and the next write sits in a transaction nobody closes. The Node driver has read better-sqlite3's live `inTransaction` all along and had the same hole; both are fixed, and both mocks now move the flag the way SQLite does. The capi check moves above the open so a build without `sqlite3_get_autocommit` does not leak the handle, and `inTransaction` is no longer `configurable`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sirtimid
added this pull request to stack #1109
September 15, 2026 22:42
# Conflicts: # packages/kernel-store/CHANGELOG.md
Contributor
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
A wasm write that failed with SQLITE_FULL left its prepared statement unreset, and SQLite refuses to bind one in that state, so every later call to the same operation threw SQLITE_MISUSE. The kv and vatstore statements now reset in `finally`, as `getKVData` and `executeQuery` already did. The real-SQLite tests now end the transaction with a real SQLITE_FULL instead of a hand-issued `ROLLBACK TRANSACTION`, which never failed a statement and so could not see this. A new Node test runs the savepoint stack clear against real better-sqlite3, and the changelog says what `main` actually did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sirtimid
added a commit
that referenced
this pull request
Oct 8, 2026
…int's transaction After SQLITE_FULL or an I/O error, SQLite can roll back the transaction a crank's savepoints were in while the crank carries on. Every write it made from then on landed outside any transaction and committed alone: the dequeue was undone but the vatstore and promise resolutions stuck, so a restart redelivered the message to a vat that had already handled it. Every driver write now goes through `guardWrite`, which refuses while the savepoint stack names a transaction SQLite no longer has. `rollbackSavepoint` and `releaseSavepoint` tell the savepoint's owner that SQLite already ended the transaction and clear the stack, which ends the refusal. Both errors carry the write that failed as `cause`, since the code that caught it may have swallowed it. The stack clear #1089 added to `beginIfNeeded` is unreachable behind the new check and goes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rekmarks-consensys-1
approved these changes
Oct 8, 2026
sirtimid
added a commit
that referenced
this pull request
Oct 8, 2026
sirtimid
added a commit
that referenced
this pull request
Oct 8, 2026
sirtimid
added a commit
that referenced
this pull request
Oct 9, 2026
Conflicts: the changelog keeps #1092's wasm COMMIT entry and drops #1089's Node entry, which this branch replaces; the corrected `rollbackSavepoint` comment sits above `discardTransaction('rollback')`; the fake driver is this branch's, with `abort` destructured where #1092 restored its assertion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The wasm driver tracked open transactions with its own
_inTxflag. SQLite can roll a transaction back on its own afterSQLITE_FULL,SQLITE_IOERRorSQLITE_NOMEM, and the flag stayed true. The nextcreateSavepointskipped itsBEGIN, so itsSAVEPOINTopened a transaction thatRELEASEcommitted, and the driver's ownCOMMITthen threwcannot commit - no transaction is active. The flag never cleared, so later releases threw the same.inTransactionis now a getter oversqlite3_get_autocommit. Init fails if the wasm build does not bind that function. A closed database reports no transaction open.beginIfNeededclears the savepoint stack when it opens a transaction, in both drivers. The Node driver has had this bug since the savepoint stack was added (feat(kernel): Handle syscall failures #520); the wasm driver would have gained it with the live getter.finally, so oneSQLITE_FULLno longer makes every later call to that operation throwSQLITE_MISUSE.Testing
wasm.transactions.test.tsand the newnodejs.transactions.test.tsrun the drivers against real SQLite and fail a write with a realSQLITE_FULL(PRAGMA max_page_count). 'takes the next crank in a transaction of its own' and 'keeps taking kv/vatstore writes after one fails' fail onmain. 'commits a write made after SQLite ends the transaction itself' fails without the wasm stack clear, and the Node test, whose write must survive closing and reopening the database, fails without the Node one.wasm.test.tsandnodejs.test.tsflip transaction state whenBEGIN/COMMIT/ABORTrun, as SQLite does, which fixed three assertions that could not fail. Anit.eachinwasm.test.tspins the reset at each of the seven bound statements.Resolves #1074
🤖 Generated with Claude Code
Note
Medium Risk
Changes transaction and savepoint handling in the kernel persistence layer; bugs here could lose or fail to commit vat state, though the PR corrects known failure modes after SQLite auto-rollback.
Overview
Fixes kernel-store SQLite drivers when SQLite implicitly rolls back a transaction (e.g.
SQLITE_FULL/ I/O errors) while the driver still thought a transaction and savepoints were active.The wasm driver stops caching transaction state in
_inTxand exposesinTransactionfromsqlite3_get_autocommit(init fails if that C API is missing).beginIfNeededin both Node and wasm clears the savepoint stack when opening a new transaction so stale names cannot block commits or causecannot commit - no transaction is activeon later savepoint releases. Wasm KV/vatstore pathsresetprepared statements infinallyso one failed step does not poison later calls withSQLITE_MISUSE.Tests add real-SQLite transaction suites (
wasm.transactions.test.ts,nodejs.transactions.test.ts) and tighten mocks soBEGIN/COMMIT/ABORTflip transaction state like SQLite. CHANGELOG also records the separatebetter-sqlite3^13 bump (Node 22+).Reviewed by Cursor Bugbot for commit 006ae98. Bugbot is set up for automated code reviews on this repo. Configure here.