charmbracelet/crush · error

failed to retire client: %w

Error message

failed to retire client: %w

What it means

RetireClient's generic failure path: the retire-client HTTP request failed (transport error or a non-404 bad status from checkStatus). The underlying cause is wrapped for inspection.

Source

Thrown at internal/client/client.go:197

// RetireClient tells the server this client has exited, releasing every
// claim it holds on every workspace. It is the client's authoritative
// goodbye: after it returns, the server refuses further workspace
// creates from this client ID, so a create whose response was lost cannot
// leave a workspace nobody can name.
//
// Servers predating the endpoint answer 404, reported as
// [ErrUnsupported] so callers can fall back to releasing by workspace ID.
func (c *Client) RetireClient(ctx context.Context) error {
	rsp, err := c.delete(ctx, "/clients/"+c.clientID, nil, nil)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()
	if err := checkStatus(rsp); err != nil {
		if errors.Is(err, ErrNotFound) {
			return fmt.Errorf("%w: %w", ErrUnsupported, err)
		}
		return fmt.Errorf("failed to retire client: %w", err)
	}
	return nil
}

// Dial opens a connection to the server using the same scheme-aware
// logic the client uses for its HTTP transport. Exposed so callers can
// reuse the dialer when they need to construct sibling HTTP transports
// (e.g. a readiness probe in the CLI).
func (c *Client) Dial(ctx context.Context, network, address string) (net.Conn, error) {
	return c.dialer(ctx, network, address)
}

func (c *Client) dialer(ctx context.Context, network, address string) (net.Conn, error) {
	d := net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}
	switch c.network {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check the wrapped cause for transport vs status failures
  2. Verify the server process is running and reachable
  3. Retry the retire call; it is idempotent from the client side
Defensive patterns

Strategy: retry

Validate before calling

// Check server reachability before calling
conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err != nil {
    return fmt.Errorf("server unreachable: %w", err)
}
conn.Close()

Try / catch

if err := c.RetireClient(ctx, id); err != nil {
    if ctx.Err() != nil {
        return err // canceled; do not retry
    }
    // retry with backoff
}

Prevention

When it happens

Trigger: Network failure while posting to the retire endpoint; server returns 4xx/5xx other than 404 from checkStatus.

Common situations: Server process died mid-request; connection refused to the server's socket/port; server bug returning 500 on retire.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/005973af6e560d77. Report an issue: GitHub.