gastownhall/beads · critical
write commit result indeterminate after connection loss (not
Error message
write commit result indeterminate after connection loss (not retried to avoid double-apply): %w
What it means
A write transaction's commit produced the ErrCommitIndeterminate sentinel: the connection was lost at commit time, so it is unknown whether the commit landed server-side. The retry loop deliberately converts this to a permanent error and never replays the callback, because replaying could double-apply the write. Before returning, the failure is recorded via recordDoltPublicationFailure for observability/recovery.
Source
Thrown at internal/storage/dolt/store.go:1158
if err == nil {
if !circuitWriteManaged(ctx) && s.breaker != nil {
s.breaker.RecordSuccess()
}
return nil
}
// Dolt's exact 1105 autocommit rollback proves the transaction did not
// land. This is the only 1105 replayed, and withRetryTx is the boundary
// that recreates the complete SQL transaction on every attempt.
if isDoltAutocommitRollbackError(err) {
doltMetrics.serializationErrors.Add(ctx, 1)
doltMetrics.writeRetries.Add(ctx, 1, metric.WithAttributes(attribute.String("type", "serialization")))
return err
}
// A commit result marked indeterminate may have landed before its
// response was lost. Never replay the callback in that case.
if errors.Is(err, ErrCommitIndeterminate) {
err = s.recordDoltPublicationFailure(ctx, err)
return backoff.Permanent(fmt.Errorf("write commit result indeterminate after connection loss (not retried to avoid double-apply): %w", err))
}
// Serialization failures (1213/1205) guarantee a server-side rollback,
// so the write never landed — safe to replay at any phase.
if isSerializationError(err) {
doltMetrics.serializationErrors.Add(ctx, 1)
doltMetrics.writeRetries.Add(ctx, 1, metric.WithAttributes(attribute.String("type", "serialization")))
return err // retryable
}
// Connection failures reaching this branch happened before commit;
// withWriteTx marks ambiguous commit response loss with the public
// ErrCommitIndeterminate sentinel above.
if isRetryableError(err) {
doltMetrics.writeRetries.Add(ctx, 1, metric.WithAttributes(attribute.String("type", "connection")))
if s.breaker != nil && isConnectionError(err) {
s.breaker.RecordFailure()
if s.breaker.State() == circuitOpen {
doltMetrics.circuitTrips.Add(ctx, 1)
return backoff.Permanent(fmt.Errorf("%w (circuit breaker tripped)", err))View on GitHub (pinned to 71377f2769)
Solutions
- Do NOT blindly re-run the write — first inspect the data (or the recorded publication failure) to determine whether the commit actually landed.
- Check the publication-failure record created by recordDoltPublicationFailure and reconcile: compare expected vs actual row state before re-applying.
- Make the write idempotent (deterministic keys/upserts) so a safe re-apply is possible next time.
- Investigate the root cause of the connection loss at commit time (server logs, network stability, timeout configuration).
Example fix
// before: naive retry can double-apply
for {
if err := applyWrite(ctx); err == nil { break }
}
// after: check whether the write landed before re-applying
err := applyWrite(ctx)
if errors.Is(err, storage.ErrCommitIndeterminate) {
if !writeAlreadyApplied(tx) { // verify server-side state first
_ = applyWrite(ctx)
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// keep writes idempotent so indeterminate outcomes are reconcilable // use deterministic primary keys / upserts instead of blind inserts const upsert = "INSERT INTO issues (id, title) VALUES (?, ?) ON DUPLICATE KEY UPDATE title = VALUES(title)"
Type guard
func isIndeterminate(err error) bool { return errors.Is(err, storage.ErrCommitIndeterminate) } Try / catch
err := store.Update(ctx, op)
if errors.Is(err, storage.ErrCommitIndeterminate) {
// check server-side state before ever re-applying
if !op.AlreadyApplied(ctx, store) {
err = store.Update(ctx, op)
}
} Prevention
- Never retry a write after ErrCommitIndeterminate without first verifying server-side state.
- Design writes to be idempotent (deterministic IDs, upsert semantics).
- Harden the network path to the Dolt server (avoid flaky proxies, tune timeouts).
- Treat every occurrence as an incident: check recordDoltPublicationFailure output for what may have landed.
When it happens
Trigger: tx.Commit() inside withRetryTx/withWriteTx returned a connection-class error (connection lost during commit response), the code classified it as indeterminate, and the retry wrapper stopped all retries and returned this message with the recorded failure wrapped inside.
Common situations: Dolt sql-server restarted exactly at commit time; network partition during a write; client read/write timeout firing while the server was still committing; proxy or load balancer dropping the connection mid-response.
Related errors
- publish working set after SQL commit: %w: %w
- failed to commit restore: %w
- commit import: %w
- failed to commit is_blocked repairs to Dolt: %w
- failed to commit dependency key repairs: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ca420778eba7840e.
Report an issue: GitHub.