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

  1. Retry the cleanup once — handle close failures are usually transient OS conditions.
  2. Check system handle usage (Task Manager / handle.exe on Windows) and close leaking processes.
  3. Reboot or free OS resources if handle exhaustion is indicated.
  4. 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

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


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