charmbracelet/crush · error

failed to get MCP states: status code %d

Error message

failed to get MCP states: status code %d

What it means

MCPGetStates got a non-200 HTTP status from /workspaces/{id}/mcp/states. The server responded but rejected the request; the code is embedded in the message. Useful for distinguishing 404 (bad id) from 401 (auth) or 5xx (server fault).

Source

Thrown at internal/client/proto.go:322

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

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Branch on the status code: 404 -> refresh workspace id; 401/403 -> re-auth; >=500 -> inspect server logs.
  2. Validate the workspace id against a fresh GetWorkspace call before polling.
  3. Fix any broken MCP server configuration server-side if 500s persist.
  4. Update client/server to matching versions.

Example fix

// before
states, err := client.MCPGetStates(ctx, wsID)
// after
states, err := client.MCPGetStates(ctx, wsID)
if err != nil {
    var statusErr *statusCodeError // if you wrap status codes
    if errors.As(err, &statusErr) && statusErr.code == http.StatusNotFound {
        return nil, fmt.Errorf("workspace %s not found", wsID)
    }
    return nil, err
}
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := client.GetWorkspace(ctx, wsID); err != nil { return fmt.Errorf("cannot query MCP states: invalid workspace: %w", err) }

Type guard

func statusCodeFrom(err error) int {
	var se interface{ StatusCode() int }
	if err != nil && errors.As(err, &se) { return se.StatusCode() }
	return 0
}

Try / catch

states, err := client.MCPGetStates(ctx, wsID)
if err != nil {
	switch statusCodeFrom(err) {
	case http.StatusNotFound: return nil, nil // no workspace/MCP states — treat as empty
	case http.StatusUnauthorized: return nil, retryAfterAuth(ctx)
	default: return nil, err
	}
}

Prevention

When it happens

Trigger: Calling MCPGetStates with an unknown workspace id (404), invalid/expired credentials (401/403), or when the server errors while collecting MCP states (500).

Common situations: Workspace recreated with a new id; auth token rotated; MCP state collection panicking server-side due to a broken MCP server config; old client hitting a restructured endpoint.

Related errors


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