gastownhall/beads · warning · errStartInterrupted

%w for %s: stop epoch advanced during startup

Error message

%w for %s: stop epoch advanced during startup

What it means

During startup, if the stop epoch advances after the listener/backend boot began but before startup completes, the proxy invokes abortInterruptedStart, which stops the backend, releases proxy.lock early, and returns this errStartInterrupted-wrapped error. Nothing has been published yet, so a clean abort avoids starving the stopper into its timeout.

Source

Thrown at internal/storage/dbproxy/proxy/server.go:220

			}
		}
	}()
	stopEpochWatch := func() {
		epochWatchCancel()
		<-epochWatchDone
	}
	defer stopEpochWatch()

	// abortInterruptedStart tears down a doomed start whose stop epoch
	// advanced mid-boot. The interrupting stop is polling proxy.lock under a
	// budget (shutdownConfirmDeadline) far smaller than a backend stop can
	// take, and nothing has been published, so release the lock BEFORE the
	// backend teardown instead of starving the stopper into its timeout.
	abortInterruptedStart := func() error {
		p.stats.IncBackendStop()
		releaseLock()
		_ = stopBackendBounded(p.server)
		return fmt.Errorf("%w for %s: stop epoch advanced during startup", errStartInterrupted, p.rootDir)
	}

	addr := fmt.Sprintf("127.0.0.1:%d", p.port)

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("listen on %s: %w", addr, err)
	}

	p.listener = ln
	defer func() { _ = ln.Close() }()
	p.stats.IncListenAndServe()
	dataPort, ok := ln.Addr().(*net.TCPAddr)
	if !ok {
		return fmt.Errorf("proxy: unexpected data listener address %T", ln.Addr())
	}

	if _, err := identity.WriteSecret(p.rootDir); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the start after the stop has fully completed
  2. Sequence start/stop in automation so they don't overlap
  3. Treat errStartInterrupted as an expected abort: verify the service is stopped rather than retrying blindly
Defensive patterns

Strategy: retry

Validate before calling

// verify the stop epoch is stable before starting
epoch1, _ := readStopEpoch(rootDir)
time.Sleep(50 * time.Millisecond)
epoch2, err := readStopEpoch(rootDir)
if err != nil || epoch1 != epoch2 { return fmt.Errorf("stop in flight") }

Try / catch

err := p.ListenAndServe(ctx)
if errors.Is(err, errStartInterrupted) { /* stop won the race; decide whether to restart */ }

Prevention

When it happens

Trigger: `bd dolt stop` advances the stop epoch while the proxy is mid-startup (after the post-lock epoch check, before publishing); detected via the startup-completion epoch check.

Common situations: Stop command racing an auto-restart; CI/tests issuing rapid start/stop cycles; an operator stopping the service at the same moment a supervisor restarts it.

Related errors


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