charmbracelet/crush · error
failed to decode session history files: %w
Error message
failed to decode session history files: %w
What it means
ListSessionHistoryFiles got HTTP 200 but the response body could not be decoded into []proto.File. The JSON decode error is wrapped with this message. Typically the body is malformed, truncated, or shaped differently than the expected array of File objects.
Source
Thrown at internal/client/proto.go:636
if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
return nil, fmt.Errorf("failed to decode session: %w", err)
}
return &sess, nil
}
// ListSessionHistoryFiles retrieves history files for a session as proto types.
func (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, sessionID string) ([]proto.File, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s/history", id, sessionID), nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to get session history files: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get session history files: status code %d", rsp.StatusCode)
}
var files []proto.File
if err := json.NewDecoder(rsp.Body).Decode(&files); err != nil {
return nil, fmt.Errorf("failed to decode session history files: %w", err)
}
return files, nil
}
// CreateSession creates a new session in a workspace as a proto type.
func (c *Client) CreateSession(ctx context.Context, id string, title string) (*proto.Session, error) {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/sessions", id), nil, jsonBody(proto.Session{Title: title}), http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return nil, fmt.Errorf("failed to create session: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to create session: status code %d", rsp.StatusCode)
}
var sess proto.Session
if err := json.NewDecoder(rsp.Body).Decode(&sess); err != nil {
return nil, fmt.Errorf("failed to decode session: %w", err)
}View on GitHub (pinned to 7944b8e522)
Solutions
- Log the wrapped error to see the exact decode failure.
- curl the endpoint and inspect the raw body to confirm the JSON shape.
- Align server and client versions so the File schema matches proto.File.
- Check for proxies/middleware rewriting responses.
- Retry on truncated (unexpected EOF) responses.
Defensive patterns
Strategy: try-catch
Type guard
func isHistoryDecodeError(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to decode session history files")
} Try / catch
files, err := client.ListSessionHistoryFiles(ctx, wsID, sessID)
if err != nil {
if isHistoryDecodeError(err) {
// inspect raw server response / align versions, then retry
return fmt.Errorf("unparseable history payload: %w", err)
}
return err
} Prevention
- Match server/client schema versions.
- Verify endpoint output with curl before integrating.
- Beware proxies returning 200 with HTML.
- Retry on unexpected-EOF truncations.
When it happens
Trigger: History endpoint returns 200 with a non-array JSON document, truncated body, empty body, or File fields whose types changed between server/client versions.
Common situations: Proxy returning 200 with HTML; server version skew changing the File schema; server returning null or an object instead of an array.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode session: %w
- failed to decode sessions: %w
- server health check failed: %s
- server shutdown failed: %s
- failed to set compact mode: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/3cd515601dcc2c62.
Report an issue: GitHub.