charmbracelet/crush · error
failed to decode LSP diagnostics: %w
Error message
failed to decode LSP diagnostics: %w
What it means
GetLSPDiagnostics got a 200 response but the body could not be decoded into map[protocol.DocumentURI][]protocol.Diagnostic. This indicates the server returned malformed, empty, or unexpected JSON rather than the documented diagnostics shape.
Source
Thrown at internal/client/proto.go:292
return true
case <-ctx.Done():
return false
}
}
// GetLSPDiagnostics retrieves LSP diagnostics for a specific LSP client.
func (c *Client) GetLSPDiagnostics(ctx context.Context, id string, lspName string) (map[protocol.DocumentURI][]protocol.Diagnostic, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/lsps/%s/diagnostics", id, lspName), nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to get LSP diagnostics: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get LSP diagnostics: status code %d", rsp.StatusCode)
}
var diagnostics map[protocol.DocumentURI][]protocol.Diagnostic
if err := json.NewDecoder(rsp.Body).Decode(&diagnostics); err != nil {
return nil, fmt.Errorf("failed to decode LSP diagnostics: %w", err)
}
return diagnostics, nil
}
// GetLSPs retrieves the LSP client states for a workspace.
func (c *Client) GetLSPs(ctx context.Context, id string) (map[string]proto.LSPClientInfo, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/lsps", id), nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to get LSPs: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get LSPs: status code %d", rsp.StatusCode)
}
var lsps map[string]proto.LSPClientInfo
if err := json.NewDecoder(rsp.Body).Decode(&lsps); err != nil {
return nil, fmt.Errorf("failed to decode LSPs: %w", err)
}View on GitHub (pinned to 7944b8e522)
Solutions
- Verify client and crush server versions match; upgrade the client if the API schema changed.
- Dump the raw body (read rsp.Body into a buffer before decoding) to inspect what was actually returned.
- Check for proxies/middleware modifying responses and bypass them.
- Retry after restarting the server if the body was truncated.
Example fix
// before
var diagnostics map[protocol.DocumentURI][]protocol.Diagnostic
json.NewDecoder(rsp.Body).Decode(&diagnostics) // opaque error
// after
body, _ := io.ReadAll(rsp.Body)
var diagnostics map[protocol.DocumentURI][]protocol.Diagnostic
if err := json.Unmarshal(body, &diagnostics); err != nil {
return nil, fmt.Errorf("failed to decode LSP diagnostics: %w (body: %.200s)", err, body)
} Defensive patterns
Strategy: type-guard
Validate before calling
// Read and sanity-check the body shape before trusting it:
body, _ := io.ReadAll(rsp.Body)
if !bytes.HasPrefix(bytes.TrimSpace(body), []byte("{")) { return errors.New("response is not a JSON object") } Type guard
func isValidDiagnostics(v map[protocol.DocumentURI][]protocol.Diagnostic) bool {
for uri, diags := range v {
if uri == "" { return false }
for _, d := range diags { if d.Message == "" { return false } }
}
return true
} Try / catch
var diags map[protocol.DocumentURI][]protocol.Diagnostic
if err := json.Unmarshal(body, &diags); err != nil {
var jerr *json.UnmarshalTypeError
if errors.As(err, &jerr) {
return nil, fmt.Errorf("diagnostics schema mismatch at %s: %w", jerr.Field, jerr)
}
return nil, fmt.Errorf("malformed diagnostics body: %w", err)
} Prevention
- Pin matching client/server versions to avoid schema drift.
- Log raw bodies on decode failure for fast diagnosis.
- Check Content-Type is application/json before decoding.
- Watch for proxies/captive portals injecting HTML into 200 responses.
When it happens
Trigger: Calling GetLSPDiagnostics when the server returns an HTML error page with 200, a truncated body, an incompatible JSON schema (field type mismatch, e.g. a string where an object is expected), or a proxy that rewrites the response.
Common situations: Client and server version mismatch after an upgrade changed the diagnostics schema; an intercepting proxy/captive portal returning HTML; server bug emitting partial output when LSP diagnostics are too large.
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 LSPs: %w
- failed to decode MCP states: %w
- failed to decode response: %w
- failed to read file: %w
- failed to marshal GraphQL request: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/30a9d9c7964a8354.
Report an issue: GitHub.