gastownhall/beads · critical · ErrCommitIndeterminate
sql commit (ignored, regular already committed): %w: %w
Error message
sql commit (ignored, regular already committed): %w: %w
What it means
After the regular SQL transaction and Dolt commit succeed, finishDoltTransaction commits the ignored-tables transaction (wisps, local_metadata, repo_mtimes, events journal when not journal-pinned). If that second COMMIT fails, the regular mutation is already durable and committed to Dolt — so the failure is again flagged with ErrCommitIndeterminate: some effects landed, the ignored-side effects (e.g. wisp rows) did not, and a naive replay could double-apply the regular side.
Source
Thrown at internal/storage/dolt/transaction.go:280
_ = tx.ignoredTx.Rollback()
}
}
if err := tx.regularTx.Commit(); err != nil {
rollbackIgnored()
return wrapSQLCommitError("sql commit (regular)", err)
}
if err := versioncontrolops.StageAndCommit(ctx, conn, tx.dirty.DirtyTables(), commitMsg, s.commitAuthorString()); err != nil {
rollbackIgnored()
return fmt.Errorf("stage and commit after regular SQL commit: %w: %w", err, ErrCommitIndeterminate)
}
if tx.journalPinned {
return nil
}
if err := tx.ignoredTx.Commit(); err != nil {
return fmt.Errorf("sql commit (ignored, regular already committed): %w: %w", err, ErrCommitIndeterminate)
}
return nil
}
// ignoredTxBorrowTimeout bounds how long a borrow of a second warm connection
// from the main pool may wait before falling back to a dedicated fresh dial. It
// keeps the second acquisition from ever waiting unboundedly while the caller
// already holds the first (regular-tx) connection, which is what makes deadlock
// impossible by construction on the borrow path.
const ignoredTxBorrowTimeout = 250 * time.Millisecond
// beginIgnoredTxOnBranch starts the ignored-tables transaction, checked out to
// the regular transaction's branch. It borrows a second warm connection from the
// main pool when one is safely available — the hosted-gateway churn fix: once the
// pool is warm this costs zero new MySQL handshakes and zero Dolt session-setup
// round-trips per write. It falls back to a dedicated single-connection pool when
// borrowing could deadlock (MaxOpenConns==1, the documented case that every
// branch-isolated test exercises) or when the pool is exhausted or a borrowedView on GitHub (pinned to 71377f2769)
Solutions
- Verify which effects landed: read back the regular rows and the wisp/ignored rows before deciding to redo anything
- Do not blind-replay; route through the library's ErrCommitIndeterminate-aware retry so the connection loss is recorded without replay
- Enable the events journal (journal-pinned single-transaction mode) to eliminate the second commit window entirely
- Check server health/network; if lock conflicts recur, reduce concurrent writers on the same rows
Example fix
// before // journal off: regular + ignored commit windows can diverge store.RunInTransaction(ctx, msg, fn) // fn mixes durable + wisp writes // after // journal-pinned mode shares one SQL tx for both planes store.SetEventsJournalEnabled(true)
Defensive patterns
Strategy: type-guard
Type guard
func isIgnoredCommitIndeterminate(err error) bool { return errors.Is(err, dolt.ErrCommitIndeterminate) } Try / catch
if err != nil && errors.Is(err, dolt.ErrCommitIndeterminate) {
// regular side is durable; check wisp/ignored rows before redoing anything
return verifyAndRepair(ctx, ids)
} Prevention
- Enable events-journal (journal-pinned) mode to collapse the two commits into one
- Avoid mixing many durable + wisp writes under journal-off mode in hot paths
- Keep borrowed/fresh ignored connections alive (ConnMaxLifetime < server idle timeout)
- Reconcile by reading back both planes' rows after any indeterminate error
When it happens
Trigger: The ignoredTx COMMIT fails after the regular side fully committed: connection death on the borrowed/fresh ignored connection, ctx canceled during commit, server rejecting the commit (lock conflict, shutdown), or the ignored session's branch was disturbed.
Common situations: Borrowed pool connection going stale between BEGIN and COMMIT; server restart mid-operation; mixed durable+wisp writes hitting contention on shared rows when journal mode is off; deadline too short for two-connection commit windows.
Related errors
- stage and commit after regular SQL commit: %w: %w
- failed to commit restore: %w
- failed to commit orphaned dependency removals: %w
- failed to commit federation peer: %w
- failed to stage %s before commit: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ac58a63b87374c3b.
Report an issue: GitHub.