{"record":{"id":"df246cb039569fa0","repo":"charmbracelet/crush","slug":"failed-to-decode-session-w","errorCode":null,"errorMessage":"failed to decode session: %w","messagePattern":"failed to decode session: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":619,"sourceCode":"\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}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to get session: status code %d\", rsp.StatusCode)\n\t}\n\tvar sess proto.Session\n\tif err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode session: %w\", err)\n\t}\n\treturn &sess, nil\n}\n\n// ListSessionHistoryFiles retrieves history files for a session as proto types.\nfunc (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, sessionID string) ([]proto.File, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/sessions/%s/history\", id, sessionID), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get session history files: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to get session history files: status code %d\", rsp.StatusCode)\n\t}\n\tvar files []proto.File\n\tif err := json.NewDecoder(rsp.Body).Decode(&files); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode session history files: %w\", err)\n\t}","sourceCodeStart":601,"sourceCodeEnd":637,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L601-L637","documentation":"GetSession successfully reached the workspace server's /workspaces/{id}/sessions/{sid} endpoint (HTTP 200), but the JSON response body could not be decoded into proto.Session. The client wraps the json.Decoder error with this message. It indicates the server returned 200 with a body that is not a valid or expected Session JSON document.","triggerScenarios":"GET /workspaces/{id}/sessions/{sid} returns 200 but the body is not JSON (e.g. an HTML error page from a proxy), is truncated, or its fields do not match proto.Session's JSON schema.","commonSituations":"A reverse proxy or auth layer intercepts the request and returns 200 with HTML; server/client version skew where Session fields changed; server bug returning empty or partial body.","solutions":["Log the wrapped error (err) to see the exact JSON decode failure (unexpected EOF, type mismatch, invalid character).","Re-run with a matching server version so the Session JSON schema matches the client's proto.Session.","Call the endpoint manually (curl) and inspect the raw 200 body for HTML/truncation.","Check for proxies/middleware that rewrite responses.","Retry the request in case of a truncated response."],"exampleFix":"// before\nsess, err := client.GetSession(ctx, wsID, sessID)\nif err != nil { return err }\n// after\nsess, err := client.GetSession(ctx, wsID, sessID)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to decode session\") {\n        // inspect/refresh server or retry\n        return fmt.Errorf(\"server returned unparseable session payload: %w\", err)\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Verify connectivity before calling:\nif err := client.ping(ctx); err != nil {\n    return fmt.Errorf(\"server unreachable: %w\", err)\n}","typeGuard":"func isDecodeError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to decode session\")\n}","tryCatchPattern":"sess, err := client.GetSession(ctx, wsID, sessID)\nif err != nil {\n    if isDecodeError(err) {\n        // server returned unparseable payload; retry or inspect server\n        return fmt.Errorf(\"malformed session response for %s: %w\", sessID, err)\n    }\n    return err\n}","preventionTips":["Keep client and server versions in sync.","Check proxies that return 200 with non-JSON bodies.","Log wrapped errors to surface the underlying JSON cause.","Retry on transient truncation errors."],"tags":["http","json-decode","client"],"backgroundTag":"json-response-decode-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}