gastownhall/beads · error
close verified backend process handle for pid %d: %w
Error message
close verified backend process handle for pid %d: %w
What it means
After successfully killing the verified orphan backend, cleanup closes the OS process handle before waiting for exit (on Windows an open handle keeps the PID allocated, breaking exit verification). This error wraps a handle.Close() failure — the handle could not be released, so cleanup aborts rather than risk a misleading exit-wait.
Source
Thrown at internal/storage/dbproxy/proxy/endpoint.go:843
err,
unverifiableProcessChecks{},
)
}
if dead {
if _, err := quarantineRecord(rootDir, server.PIDFileName, time.Now()); err != nil {
return fmt.Errorf("quarantine dead backend record: %w", err)
}
return nil
}
if err := handle.Kill(); err != nil {
_ = handle.Close()
return fmt.Errorf("kill verified orphan backend pid %d from %s: %w", pf.Pid, recordPath, err)
}
// Close before waiting: an open handle on Windows keeps the dead PID
// allocated, so procid.Verify would keep matching it and the exit wait
// below would always time out.
if err := handle.Close(); err != nil {
return fmt.Errorf("close verified backend process handle for pid %d: %w", pf.Pid, err)
}
if err := waitForRecordedProcessExit(pf, backendExitTimeout); err != nil {
return fmt.Errorf("wait for verified orphan backend pid %d: %w", pf.Pid, err)
}
if _, err := quarantineRecord(rootDir, server.PIDFileName, time.Now()); err != nil {
return fmt.Errorf("quarantine orphan backend record: %w", err)
}
return nil
}
type unverifiableProcessChecks struct {
LiveEstablished bool
LegacyProxy bool
}
func unverifiableProcessError(
operation string,
recordPath string,View on GitHub (pinned to 71377f2769)
Solutions
- Retry the cleanup once — handle close failures are usually transient OS conditions.
- Check system handle usage (Task Manager / handle.exe on Windows) and close leaking processes.
- Reboot or free OS resources if handle exhaustion is indicated.
- As a last resort, since Kill already succeeded, wait for the pid to disappear, then manually quarantine: mv <recordPath> <recordPath>.stale-<unix-ts>.
Example fix
// before Error: close verified backend process handle for pid 4242: os: process already finished // after $ ps -p 4242 || true # confirm dead $ mv /ws/.beads/dbproxy/dolt-backend.pid /ws/.beads/dbproxy/dolt-backend.pid.stale-$(date +%s)
Defensive patterns
Strategy: retry
Try / catch
var retried bool
retry:
if err := cleanupOrphanBackend(rootDir); err != nil && strings.Contains(err.Error(), "close verified backend process handle") && !retried {
retried = true
time.Sleep(500 * time.Millisecond)
goto retry
} Prevention
- Retry once — handle-close failures are typically transient.
- On Windows, watch for handle leaks with handle.exe / Process Explorer.
- Keep OS resource limits healthy (few stale processes/handles).
- Since Kill already succeeded, verify the pid is dead before manual quarantine.
When it happens
Trigger: handle.Kill() succeeded but handle.Close() returns an error at internal/storage/dbproxy/proxy/endpoint.go:842-843 (OS-level handle release failure).
Common situations: OS handle-table pressure or duplicated handle ownership on Windows; driver/runtime errors closing the process handle; rare resource-exhaustion conditions (too many open handles).
Related errors
- reading another process's command line is not supported on w
- open process %d: %w
- workspacegate: close %s: %w
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/bcfc0e6983ac29a7.
Report an issue: GitHub.