charmbracelet/crush · error

failed to get LSPs: status code %d

Error message

failed to get LSPs: status code %d

What it means

GetLSPs got an HTTP response whose status code was not 200 from /workspaces/{id}/lsps. The server deliberately rejected the request; the numeric status is included in the message. No LSP state map is returned.

Source

Thrown at internal/client/proto.go:305

	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)
	}
	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)
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check the status code in the message and handle 404 by refreshing the workspace id (re-create or list workspaces).
  2. Re-authenticate for 401/403 responses.
  3. Restart the server / check server logs for 5xx errors.
  4. Align client and server versions so the endpoint exists.

Example fix

// before
lsps, err := client.GetLSPs(ctx, staleWsID)
// after
ws, err := client.GetWorkspace(ctx, currentWsID)
if err != nil { return nil, err }
lsps, err := client.GetLSPs(ctx, ws.ID)
if err != nil {
    if isNotFoundStatus(err) { return nil, fmt.Errorf("workspace %s gone; re-open it", currentWsID) }
    return nil, err
}
Defensive patterns

Strategy: validation

Validate before calling

ws, err := client.GetWorkspace(ctx, wsID)
if err != nil { return fmt.Errorf("workspace %s invalid before GetLSPs: %w", wsID, err) }

Type guard

func isNotFoundStatus(err error) bool {
	var se interface{ StatusCode() int }
	return err != nil && errors.As(err, &se) && (se.StatusCode() == 404 || se.StatusCode() == 410)
}

Try / catch

lsps, err := client.GetLSPs(ctx, wsID)
if err != nil {
	if isNotFoundStatus(err) {
		return nil, ErrWorkspaceGone // caller recreates/reopens the workspace
	}
	return nil, err
}

Prevention

When it happens

Trigger: Calling GetLSPs/LSPGetStates with a nonexistent workspace id (404), expired auth (401/403), a server fault (500), or a client/server version mismatch where the lsps endpoint moved (404/405).

Common situations: Workspace closed/timed out on the server side; querying with an id from a previous session; reverse proxy returning 401; running an older client against a newer server.

Related errors


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