{"record":{"id":"ff16574f1877228b","repo":"charmbracelet/crush","slug":"failed-to-get-messages-w","errorCode":null,"errorMessage":"failed to get messages: %w","messagePattern":"failed to get messages: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":594,"sourceCode":"// InitiateAgentProcessing triggers agent initialization on the server.\nfunc (c *Client) InitiateAgentProcessing(ctx context.Context, id string, interactive bool) error {\n\tbody := jsonBody(proto.AgentInitRequest{Interactive: interactive})\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/agent/init\", id), nil, body, http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to initiate session agent processing: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"failed to initiate session agent processing: status code %d\", rsp.StatusCode)\n\t}\n\treturn nil\n}\n\n// ListMessages retrieves all messages for a session as proto types.\nfunc (c *Client) ListMessages(ctx context.Context, id string, sessionID string) ([]proto.Message, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/sessions/%s/messages\", id, sessionID), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get messages: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to get messages: status code %d\", rsp.StatusCode)\n\t}\n\tvar msgs []proto.Message\n\tif err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {\n\t\treturn nil, fmt.Errorf(\"failed to decode messages: %w\", err)\n\t}\n\treturn msgs, nil\n}\n\n// GetSession retrieves a specific session as a proto type.\nfunc (c *Client) GetSession(ctx context.Context, id string, sessionID string) (*proto.Session, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/sessions/%s\", id, sessionID), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get session: %w\", err)\n\t}","sourceCodeStart":576,"sourceCodeEnd":612,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L576-L612","documentation":"Thrown by Client.ListMessages in internal/client/proto.go when the HTTP GET to /workspaces/{id}/sessions/{sessionID}/messages fails at the transport level (no HTTP response at all). The wrapped c.post/c.get error is returned, indicating connectivity, DNS, timeout, or context cancellation. Called during restoreModelFromSession when restoring a TUI session.","triggerScenarios":"Calling ListMessages (or triggering restoreModelFromSession) when the server is down or restarted, the network drops mid-request, the context times out or is canceled, or DNS fails for the server host.","commonSituations":"Resuming a session after the server crashed; flaky Wi-Fi/VPN dropping the request; server address changed between runs; client configured against a dead port; user cancels restore and the context is canceled.","solutions":["Inspect the wrapped error to distinguish cancellation/timeout from dial failures.","Verify the crush server is running and reachable, then retry the restore.","If the timeout is too short, retry with a longer context deadline.","Fix the configured server address if it no longer points at a live server."],"exampleFix":"// before\nmsgs, err := client.ListMessages(ctx, wsID, sessionID)\nif err != nil {\n    return err\n}\n// after: retry transient transport errors during session restore\nvar msgs []proto.Message\nerr := retry(3, 500*time.Millisecond, func() error {\n    var e error\n    msgs, e = client.ListMessages(ctx, wsID, sessionID)\n    return e\n})","handlingStrategy":"retry","validationCode":"// Pre-check server reachability before restoring session messages\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\nif _, err := client.ListSessions(ctx, wsID); err != nil {\n    return fmt.Errorf(\"cannot restore session: server unreachable: %w\", err)\n}","typeGuard":"func isRetryableTransport(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) || errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"msgs, err := client.ListMessages(ctx, wsID, sessionID)\nif err != nil && isRetryableTransport(err) {\n    backoff := 500 * time.Millisecond\n    for i := 0; i < 3; i++ {\n        time.Sleep(backoff)\n        if msgs, err = client.ListMessages(ctx, wsID, sessionID); err == nil {\n            break\n        }\n        backoff *= 2\n    }\n}","preventionTips":["Retry ListMessages on net.Error/timeouts — message listing is idempotent","Validate server connectivity once at session-restore startup","Use a generous context timeout; message payloads can be large","Keep the workspace id stable across restarts to avoid wrong-host requests"],"tags":["network","http-client","messages","session-restore"],"backgroundTag":"http-request-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}