charmbracelet/crush · error
failed to decode MCP states: %w
Error message
failed to decode MCP states: %w
What it means
MCPGetStates received HTTP 200 but the body failed to decode into map[string]proto.MCPClientInfo. The JSON did not match the expected MCP client-state schema — shape drift, truncation, or non-JSON content.
Source
Thrown at internal/client/proto.go:326
if err := json.NewDecoder(rsp.Body).Decode(&lsps); err != nil {
return nil, fmt.Errorf("failed to decode LSPs: %w", err)
}
return lsps, nil
}
// MCPGetStates retrieves the MCP client states for a workspace.
func (c *Client) MCPGetStates(ctx context.Context, id string) (map[string]proto.MCPClientInfo, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/mcp/states", id), nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to get MCP states: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get MCP states: status code %d", rsp.StatusCode)
}
var states map[string]proto.MCPClientInfo
if err := json.NewDecoder(rsp.Body).Decode(&states); err != nil {
return nil, fmt.Errorf("failed to decode MCP states: %w", err)
}
return states, nil
}
// MCPPendingAuth retrieves the MCP servers awaiting OAuth authentication
// for a workspace.
func (c *Client) MCPPendingAuth(ctx context.Context, id string) ([]proto.MCPPendingAuthServer, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/mcp/pending-auth", id), nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to get MCP pending auth: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get MCP pending auth: status code %d", rsp.StatusCode)
}
var pending []proto.MCPPendingAuthServer
if err := json.NewDecoder(rsp.Body).Decode(&pending); err != nil {
return nil, fmt.Errorf("failed to decode MCP pending auth: %w", err)View on GitHub (pinned to 7944b8e522)
Solutions
- Sync client and server versions so proto.MCPClientInfo matches the server output.
- Log the raw response body with the decode error to pinpoint the mismatched field.
- Remove/bypass intermediaries that rewrite the body.
- Retry after server restart if truncation is the cause.
Example fix
// before
var states map[string]proto.MCPClientInfo
if err := json.NewDecoder(rsp.Body).Decode(&states); err != nil { return nil, err }
// after
body, _ := io.ReadAll(rsp.Body)
var states map[string]proto.MCPClientInfo
if err := json.Unmarshal(body, &states); err != nil {
return nil, fmt.Errorf("failed to decode MCP states: %w (body: %.200s)", err, body)
} Defensive patterns
Strategy: validation
Validate before calling
body, _ := io.ReadAll(rsp.Body)
ct := rsp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") || len(bytes.TrimSpace(body)) == 0 {
return errors.New("MCP states response missing or not JSON")
} Type guard
func isMCPStatesShape(v any) bool {
m, ok := v.(map[string]proto.MCPClientInfo)
return ok && m != nil
} Try / catch
var states map[string]proto.MCPClientInfo
if err := json.Unmarshal(body, &states); err != nil {
return nil, fmt.Errorf("MCP states decode failed; check client/server versions: %w", err)
} Prevention
- Verify Content-Type before decoding.
- Keep proto.MCPClientInfo in sync with the server's JSON schema.
- Log raw bodies on failure to spot truncation or HTML injection.
- Retry after server restart if large payloads get truncated.
When it happens
Trigger: Calling MCPGetStates when the server emits a changed/incompatible MCPClientInfo JSON shape, a proxy injects content, the body is truncated, or an error page slipped through with a 200 status.
Common situations: Partial upgrade: new server with old client proto structs (or vice versa); auth proxies adding fields that break strict decoding is rare but HTML injection is common; huge state maps getting truncated by an intermediary.
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 LSP diagnostics: %w
- failed to decode LSPs: %w
- failed to decode response: %w
- session ID is required for creating a new file
- mcp '%s' not found in configuration
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/b858bd267f2a6632.
Report an issue: GitHub.