gastownhall/beads · error

proxy.ForceStopUnverified: resolve workspace identity: %w

Error message

proxy.ForceStopUnverified: resolve workspace identity: %w

What it means

ForceStopUnverified is the destructive path for legacy/unverifiable records only. This error wraps a failure to compute the workspace RootID via identity.RootID(rootDir), which is needed to prove the record is NOT a modern verifiable one. The library deliberately fails closed: it will not route a possibly-verifiable record into the destructive force path.

Source

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

		return nil, fmt.Errorf("proxy.ForceStopUnverified: read %s: %w", report.RecordPath, err)
	}
	if record == nil {
		return nil, nil
	}
	report.RecordFound = true
	report.PID = record.Pid
	return record, nil
}

func requireUnverifiableRecord(rootDir string, record *pidfile.PidFile, wantKind string) error {
	if err := record.ValidateV2(wantKind); err != nil {
		return nil
	}
	rootID, err := identity.RootID(rootDir)
	if err != nil {
		// Failing open here would route a possibly-verifiable record into the
		// destructive force path; surface the identity failure instead.
		return fmt.Errorf("proxy.ForceStopUnverified: resolve workspace identity: %w", err)
	}
	if record.RootID == rootID {
		return errors.New(
			"proxy.ForceStopUnverified: record has a verifiable v2 workspace identity; use proxy.Shutdown",
		)
	}
	return nil
}

func inspectAndStopUnverifiedPID(rootDir string, pid int, deadline time.Time, report *ForceStopReport) error {
	if pid <= 0 {
		return fmt.Errorf("proxy.ForceStopUnverified: record %s has invalid pid %d", report.RecordPath, pid)
	}
	// One stable handle covers inspection and signaling, so the PID cannot be
	// recycled between the executable check and the kill on platforms with a
	// pinning primitive (Linux pidfd, Windows process handle).
	proc, gone, err := openUnverifiedProcess(pid)
	if err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause (%w); restore or regenerate the workspace identity metadata if it is missing/corrupt
  2. Use proxy.Shutdown instead of ForceStopUnverified if the record actually has a verifiable v2 identity — the follow-on error states this explicitly
  3. Ensure rootDir is the correct workspace root containing the identity files
  4. If migrating versions, complete the workspace migration so identity.RootID succeeds before force-stopping

Example fix

// before
report, err := proxy.ForceStopUnverified(rootDir)
// after
if id, idErr := identity.RootID(rootDir); idErr == nil {
    _ = id
    err = proxy.Shutdown(rootDir) // verifiable workspace: use graceful path
} else {
    err = proxy.ForceStopUnverified(rootDir)
}
Defensive patterns

Strategy: fallback

Validate before calling

_, idErr := identity.RootID(rootDir)
identityResolvable := idErr == nil

Try / catch

report, err := proxy.ForceStopUnverified(rootDir)
if err != nil && strings.Contains(err.Error(), "resolve workspace identity") {
    // fail-closed by design; do not force-stop blind. Investigate identity files.
    fmt.Fprintf(os.Stderr, "cannot verify record: %v; use graceful shutdown or fix identity\n", err)
    return err
}

Prevention

When it happens

Trigger: Calling ForceStopUnverified when requireUnverifiableRecord calls identity.RootID(rootDir) and it errors (e.g. workspace identity file missing/corrupt, unreadable directory), so verifiability of the record cannot be established.

Common situations: Workspaces partially migrated between record format versions (v1 legacy vs v2 identity), identity metadata deleted or corrupted, or the rootDir passed is not the actual workspace root.

Related errors


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