{"record":{"id":"005973af6e560d77","repo":"charmbracelet/crush","slug":"failed-to-retire-client-w","errorCode":null,"errorMessage":"failed to retire client: %w","messagePattern":"failed to retire client: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/client.go","lineNumber":197,"sourceCode":"// RetireClient tells the server this client has exited, releasing every\n// claim it holds on every workspace. It is the client's authoritative\n// goodbye: after it returns, the server refuses further workspace\n// creates from this client ID, so a create whose response was lost cannot\n// leave a workspace nobody can name.\n//\n// Servers predating the endpoint answer 404, reported as\n// [ErrUnsupported] so callers can fall back to releasing by workspace ID.\nfunc (c *Client) RetireClient(ctx context.Context) error {\n\trsp, err := c.delete(ctx, \"/clients/\"+c.clientID, nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rsp.Body.Close()\n\tif err := checkStatus(rsp); err != nil {\n\t\tif errors.Is(err, ErrNotFound) {\n\t\t\treturn fmt.Errorf(\"%w: %w\", ErrUnsupported, err)\n\t\t}\n\t\treturn fmt.Errorf(\"failed to retire client: %w\", err)\n\t}\n\treturn nil\n}\n\n// Dial opens a connection to the server using the same scheme-aware\n// logic the client uses for its HTTP transport. Exposed so callers can\n// reuse the dialer when they need to construct sibling HTTP transports\n// (e.g. a readiness probe in the CLI).\nfunc (c *Client) Dial(ctx context.Context, network, address string) (net.Conn, error) {\n\treturn c.dialer(ctx, network, address)\n}\n\nfunc (c *Client) dialer(ctx context.Context, network, address string) (net.Conn, error) {\n\td := net.Dialer{\n\t\tTimeout:   30 * time.Second,\n\t\tKeepAlive: 30 * time.Second,\n\t}\n\tswitch c.network {","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/client.go#L179-L215","documentation":"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.","triggerScenarios":"Network failure while posting to the retire endpoint; server returns 4xx/5xx other than 404 from checkStatus.","commonSituations":"Server process died mid-request; connection refused to the server's socket/port; server bug returning 500 on retire.","solutions":["Check the wrapped cause for transport vs status failures","Verify the server process is running and reachable","Retry the retire call; it is idempotent from the client side"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check server reachability before calling\nconn, err := net.DialTimeout(\"tcp\", addr, 2*time.Second)\nif err != nil {\n    return fmt.Errorf(\"server unreachable: %w\", err)\n}\nconn.Close()","typeGuard":null,"tryCatchPattern":"if err := c.RetireClient(ctx, id); err != nil {\n    if ctx.Err() != nil {\n        return err // canceled; do not retry\n    }\n    // retry with backoff\n}","preventionTips":["Use a context with a sane timeout","Retry idempotent retire calls on transient failures","Monitor server process health"],"tags":["http","network","client-server"],"backgroundTag":"http-request-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}