charmbracelet/crush · error

failed to decode session: %w

Error message

failed to decode session: %w

What it means

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.

Source

Thrown at internal/client/proto.go:619

	if err := json.NewDecoder(rsp.Body).Decode(&msgs); err != nil && !errors.Is(err, io.EOF) {
		return nil, fmt.Errorf("failed to decode messages: %w", err)
	}
	return msgs, nil
}

// GetSession retrieves a specific session as a proto type.
func (c *Client) GetSession(ctx context.Context, id string, sessionID string) (*proto.Session, error) {
	rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/sessions/%s", id, sessionID), nil, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get session: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to get 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)
	}
	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)
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Log the wrapped error (err) to see the exact JSON decode failure (unexpected EOF, type mismatch, invalid character).
  2. Re-run with a matching server version so the Session JSON schema matches the client's proto.Session.
  3. Call the endpoint manually (curl) and inspect the raw 200 body for HTML/truncation.
  4. Check for proxies/middleware that rewrite responses.
  5. Retry the request in case of a truncated response.

Example fix

// before
sess, err := client.GetSession(ctx, wsID, sessID)
if err != nil { return err }
// after
sess, err := client.GetSession(ctx, wsID, sessID)
if err != nil {
    if strings.Contains(err.Error(), "failed to decode session") {
        // inspect/refresh server or retry
        return fmt.Errorf("server returned unparseable session payload: %w", err)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify connectivity before calling:
if err := client.ping(ctx); err != nil {
    return fmt.Errorf("server unreachable: %w", err)
}

Type guard

func isDecodeError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "failed to decode session")
}

Try / catch

sess, err := client.GetSession(ctx, wsID, sessID)
if err != nil {
    if isDecodeError(err) {
        // server returned unparseable payload; retry or inspect server
        return fmt.Errorf("malformed session response for %s: %w", sessID, err)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


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