gastownhall/beads · error

proxy.ForceStopUnverified: record %s has invalid pid %d

Error message

proxy.ForceStopUnverified: record %s has invalid pid %d

What it means

inspectAndStopUnverifiedPID validates the PID from the force-stop record and rejects non-positive values because they can never name a real process. The library throws this when the record contains pid <= 0, indicating a corrupt or malformed record rather than a stoppable target.

Source

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

		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 {
		return fmt.Errorf("proxy.ForceStopUnverified: open pid %d: %w", pid, err)
	}
	if gone {
		report.ProcessWasGone = true
		return nil
	}
	defer proc.close()

	executable, gone, err := proc.executableBasename()
	if err != nil {
		return fmt.Errorf("proxy.ForceStopUnverified: inspect executable for pid %d: %w", pid, err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Delete the malformed record file (<rootDir> PID/lock record path) and retry, since it names no real process
  2. Verify with ps that no live proxy exists for the workspace; if none, the record is safely removable
  3. Fix whatever wrote the placeholder PID (check for prior crash during record write)
  4. Re-run the normal bd command to regenerate a valid record

Example fix

// before
pid := parsePid(data) // may yield 0
_ = inspectAndStopUnverifiedPID(rootDir, pid, deadline, report)
// after
pid := parsePid(data)
if pid <= 0 {
    _ = os.Remove(report.RecordPath)
    return nil
}
_ = inspectAndStopUnverifiedPID(rootDir, pid, deadline, report)
Defensive patterns

Strategy: validation

Validate before calling

func recordPidValid(path string) bool {
    data, err := os.ReadFile(path)
    if err != nil {
        return false
    }
    pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
    return err == nil && pid > 0
}

Type guard

func isPositivePid(n int) bool { return n > 0 }

Try / catch

report, err := proxy.ForceStopUnverified(rootDir)
if err != nil && strings.Contains(err.Error(), "has invalid pid") {
    // Record names no real process; remove it and retry
    if rec := report.RecordPath; rec != "" {
        _ = os.Remove(rec)
        report, err = proxy.ForceStopUnverified(rootDir)
    }
}

Prevention

When it happens

Trigger: Calling ForceStopUnverified on a record whose decoded pid is 0 or negative — e.g. a zero-initialized or partially written PID file that still parsed.

Common situations: A PID file truncated to a bare '0' or '-1' after a crash, manual editing, or a tool writing placeholder values.

Related errors


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