gastownhall/beads · error

proxy.ForceStopUnverified: at most one options value is allo

Error message

proxy.ForceStopUnverified: at most one options value is allowed

What it means

ForceStopUnverified accepts variadic ForceStopOptions but is defined to take at most one options value. Passing more than one is a programming error and is rejected up front before any destructive work. This keeps the API contract unambiguous about timeout configuration.

Source

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

// unverifiable proxy or backend record. It applies the same procedure to the
// proxy record (proxy.pid) and the backend record (proxy-child.pid): a
// pre-v2 managed-local deployment leaves BOTH as legacy records, so covering
// only the proxy record would lock the advertised recovery out of the real
// upgrade topology.
//
// For each record: when its lock is free, the function holds it while
// inspecting, signaling, and quarantining the unchanged record. When the lock
// is held (the usual pre-upgrade-proxy case), it first inspects and signals
// the live PID, waits for the lock to become free, then quarantines only if
// 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,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Pass at most one ForceStopOptions value
  2. Merge desired fields into a single ForceStopOptions struct
  3. Call with no options to use the default shutdownConfirmDeadline

Example fix

// before
report, err := proxy.ForceStopUnverified(root, proxy.ForceStopOptions{Timeout: 5 * time.Second}, proxy.ForceStopOptions{})
// after
report, err := proxy.ForceStopUnverified(root, proxy.ForceStopOptions{Timeout: 5 * time.Second})
Defensive patterns

Strategy: validation

Validate before calling

func safeForceStop(root string, opts ...proxy.ForceStopOptions) (proxy.ForceStopReport, error) {
    if len(opts) > 1 {
        opts = opts[:1]
    }
    return proxy.ForceStopUnverified(root, opts...)
}

Prevention

When it happens

Trigger: Calling proxy.ForceStopUnverified(root, opt1, opt2) with two or more ForceStopOptions values.

Common situations: Copy-paste refactors that append an extra options value; wrapping helpers that spread plus pass an explicit option.

Related errors


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