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 borrowed

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify which effects landed: read back the regular rows and the wisp/ignored rows before deciding to redo anything
  2. Do not blind-replay; route through the library's ErrCommitIndeterminate-aware retry so the connection loss is recorded without replay
  3. Enable the events journal (journal-pinned single-transaction mode) to eliminate the second commit window entirely
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/ac58a63b87374c3b. Report an issue: GitHub.