gastownhall/beads · error

re-check proxy stop epoch before publish: %w

Error message

re-check proxy stop epoch before publish: %w

What it means

Wraps an error from the final stopEpochChanged re-check performed just before publishing proxy.pid. To avoid publishing a pidfile for a proxy that a concurrent stop is already tearing down, the epoch is re-read after cancelling the epoch watcher; if that read itself fails (not merely 'changed'), startup aborts with this error and the backend is stopped.

Source

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

	identReply.UpstreamID = upstreamID
	identReply.PID = os.Getpid()
	identReply.Birth = string(birth)
	identReply.ControlPort = control.Port()
	identMu.Unlock()

	// Last fence before publishing: the spawn marker was cleared when this
	// process took proxy.lock, so a `bd dolt stop` that began during a slow
	// backend start has no record of this attempt. It did advance the stop
	// epoch first, so re-check it here and abort instead of publishing a
	// running proxy after that stop returned. The startup epoch watcher is
	// stopped (synchronously) first: past this fence a stop finds the
	// published proxy.pid and stops the proxy through it, so a watcher
	// cancellation must not race the publish.
	stopEpochWatch()
	if changed, err := stopEpochChanged(p.rootDir, p.stopEpoch); err != nil {
		p.stats.IncBackendStop()
		_ = stopBackendBounded(p.server)
		return fmt.Errorf("re-check proxy stop epoch before publish: %w", err)
	} else if changed {
		return abortInterruptedStart()
	}

	if err := pidfile.Write(p.rootDir, PIDFileName, pidfile.PidFile{
		Pid:         os.Getpid(),
		Port:        dataPort.Port,
		UpstreamID:  upstreamID,
		Schema:      pidfile.SchemaV2,
		Kind:        pidfile.KindProxy,
		Birth:       string(birth),
		RootID:      rootID,
		ControlPort: control.Port(),
	}); err != nil {
		p.stats.IncBackendStop()
		_ = stopBackendBounded(p.server)
		return fmt.Errorf("write pid file: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check disk health and filesystem errors in system logs (dmesg/journalctl)
  2. Verify rootDir and the epoch state file are readable by the proxy process
  3. Ensure nothing external (cleaner job, another tool) is deleting files in rootDir while the proxy starts
  4. Retry startup once the storage issue is resolved
Defensive patterns

Strategy: validation

Validate before calling

// Verify rootDir state files are readable before start
f, err := os.Open(filepath.Join(rootDir, "stop-epoch"))
if err != nil {
    return fmt.Errorf("cannot read stop-epoch state: %w", err)
}
f.Close()

Try / catch

if err := p.ListenAndServe(ctx); err != nil {
    if strings.Contains(err.Error(), "re-check proxy stop epoch") {
        log.Fatalf("storage I/O failure during startup: %v", err)
    }
    log.Fatal(err)
}

Prevention

When it happens

Trigger: The stopEpochChanged(p.rootDir, p.stopEpoch) call after stopEpochWatch() returns an error - usually an I/O failure reading the stop-epoch state file under rootDir.

Common situations: rootDir became unwritable/unreadable (volume detached, permissions flipped); disk I/O errors on the host; concurrent deletion of the proxy state directory by an external cleaner.

Related errors


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