gastownhall/beads · error

proxy.ForceStopUnverified: publish stop epoch: %w

Error message

proxy.ForceStopUnverified: publish stop epoch: %w

What it means

ForceStopUnverified publishes a stop epoch to coordinate force-stop attempts, and this error wraps any failure of advanceStopEpoch (e.g. inability to write the epoch file in the database root). It is thrown before any process is signaled, aborting the force-stop so concurrent stop attempts do not proceed uncoordinated.

Source

Thrown at internal/storage/dbproxy/proxy/force_stop.go:68

// the record is unchanged. Both flows accept an already-gone recorded
// process. An unverified live PID is never signaled unless its executable
// basename is exactly bd or dolt (with an optional .exe suffix) AND its
// command line scopes it to this workspace; where the platform cannot
// establish that scope, force-stop refuses rather than guessing.
func ForceStopUnverified(rootDir string, opts ...ForceStopOptions) (ForceStopReport, error) {
	report := ForceStopReport{RecordPath: pidfile.Path(rootDir, PIDFileName)}
	if len(opts) > 1 {
		return report, errors.New("proxy.ForceStopUnverified: at most one options value is allowed")
	}
	timeout := shutdownConfirmDeadline
	if len(opts) == 1 && opts[0].Timeout != 0 {
		timeout = opts[0].Timeout
	}
	if timeout <= 0 {
		return report, fmt.Errorf("proxy.ForceStopUnverified: timeout must be positive, got %s", timeout)
	}
	if err := advanceStopEpoch(rootDir); err != nil {
		return report, fmt.Errorf("proxy.ForceStopUnverified: publish stop epoch: %w", err)
	}

	proxyErr := forceStopRecord(rootDir, LockFileName, PIDFileName, pidfile.KindProxy, timeout, &report)

	backendReport := ForceStopReport{RecordPath: pidfile.Path(rootDir, server.PIDFileName)}
	backendErr := forceStopRecord(
		rootDir,
		server.LockFileName,
		server.PIDFileName,
		pidfile.KindDoltBackend,
		timeout,
		&backendReport,
	)
	if backendReport.RecordFound || backendErr != nil {
		report.Backend = &backendReport
	}
	return report, errors.Join(proxyErr, backendErr)
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify rootDir exists and is writable by the current user (ls -ld, touch a test file)
  2. Check the wrapped cause (%w) for the underlying os.MkdirAll/WriteFile error and fix permissions or free disk space accordingly
  3. Pass the correct workspace root directory path to ForceStopUnverified
  4. Re-run after the filesystem becomes writable (e.g. remount rw)

Example fix

// before
report, err := proxy.ForceStopUnverified(rootDir)
// after
if info, statErr := os.Stat(rootDir); statErr != nil || !info.IsDir() {
    return fmt.Errorf("workspace root %s is not an accessible directory", rootDir)
}
report, err := proxy.ForceStopUnverified(rootDir)
Defensive patterns

Strategy: validation

Validate before calling

func rootWritable(dir string) error {
    info, err := os.Stat(dir)
    if err != nil {
        return err
    }
    if !info.IsDir() {
        return fmt.Errorf("%s is not a directory", dir)
    }
    return unix.Access(dir, unix.W_OK)
}

Try / catch

report, err := proxy.ForceStopUnverified(rootDir)
if err != nil && strings.Contains(err.Error(), "publish stop epoch") {
    fmt.Fprintf(os.Stderr, "check %s is writable: %v\n", rootDir, err)
    return err
}

Prevention

When it happens

Trigger: Calling proxy.ForceStopUnverified when advanceStopEpoch(rootDir) fails — typically a read-only or missing rootDir, permission denied writing the epoch file, or an I/O error on the filesystem holding the workspace.

Common situations: Running against a database directory mounted read-only, a wrong rootDir path that does not exist, disk-full conditions, or a container running as a different UID than the directory owner.

Related errors


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