gastownhall/beads · critical

ErrCircuitOpen

ErrCircuitOpen

Error message

dolt circuit breaker is open: server appears down, failing fast (cooldown %s)

What it means

ErrCircuitOpen is a sentinel error from the file-backed Dolt circuit breaker. After repeated failures (notably indeterminate commit failures) the breaker opens and fails fast for a cooldown period (circuitCooldown) instead of hammering an unreachable server. It signals the Dolt SQL server is believed down; the breaker is scoped per database and only active for a concrete resolved port.

Source

Thrown at internal/storage/dolt/circuit.go:91

}

// circuitBreaker manages the circuit breaker for a specific Dolt server
// host:port:database combination. Using per-database granularity prevents
// degradation in one project from tripping the breaker for all worktrees
// sharing the same server (GH#3140).
//
// It uses a file under os.TempDir() for cross-process state sharing and an in-process
// mutex for thread safety within a single process.
type circuitBreaker struct {
	host     string
	port     int
	database string
	filePath string
	mu       sync.Mutex
}

// ErrCircuitOpen is returned when the circuit breaker is open and rejecting requests.
var ErrCircuitOpen = fmt.Errorf("dolt circuit breaker is open: server appears down, failing fast (cooldown %s)", circuitCooldown)

// maybeNewCircuitBreaker returns a file-backed circuit breaker only for a
// concrete port. Port 0 means "not yet resolved" during standalone auto-start,
// and sharing breaker state on port 0 poisons every fresh init on the machine.
// The database parameter scopes the breaker to a specific project so that
// degradation in one database doesn't trip the breaker for others (GH#3140).
func maybeNewCircuitBreaker(host string, port int, database string) *circuitBreaker {
	if port <= 0 {
		return nil
	}
	return newCircuitBreaker(host, port, database)
}

// circuitBreakerDir returns the dedicated directory for circuit breaker state
// files. Using a subdirectory avoids scanning all of the temp root (which may
// contain millions of entries) when cleaning up stale breaker files on
// startup. Derived from os.TempDir() so it is correct on every platform:
// hardcoding "/tmp" resolved to C:\tmp on Windows, silently accumulating

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the Dolt server is running and reachable (e.g. `dolt sql-server` status / `bd doctor`) and restart it if needed
  2. Wait for the cooldown period to elapse — the breaker will half-open and allow retry attempts automatically
  3. Check you are not sharing a stale breaker file from another database/port; the breaker is per-database and per-port, so a resolved concrete port is required
  4. Fix the underlying connectivity issue (port config, process down) — the breaker only resets behavior, not the root cause

Example fix

// before
// writes fail fast with ErrCircuitOpen while server is down
// after
# restart the server, then wait out the cooldown before retrying
dolt sql-server &
# retry the bd command after cooldown
Defensive patterns

Strategy: retry

Validate before calling

// check server reachability before issuing writes
// e.g. run `bd doctor` or a cheap query against dolt sql-server;
// if the breaker file marks the port open, back off instead of writing

Try / catch

if err := store.UpdateIssue(ctx, id, opts); err != nil {
    if errors.Is(err, dolt.ErrCircuitOpen) {
        // fail fast; wait out the cooldown, verify server health, then retry
        time.Sleep(cooldown); if serverHealthy() { retryWrite() }
    }
}

Prevention

When it happens

Trigger: Any Dolt-backed write/read (update, claim, delete, wisp dependency ops, WithRetryTx) while the breaker is open after the failure threshold tripped — e.g. consecutive indeterminate commit errors or connection failures to the dolt sql-server.

Common situations: dolt sql-server crashed or was stopped mid-session; wrong port after auto-start; network partition to a remote Dolt server; a prior indeterminate commit sequence tripped the breaker and the cooldown has not elapsed.

Related errors


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