This is an automated email from the ASF dual-hosted git repository. enzomartellucci pushed a commit to branch enxdev/fix/matrix-chart-error-bubbling in repository https://gitbox.apache.org/repos/asf/superset.git
commit 28f33c81f5ef5678e8e7ed04f5d8b013502cc59f Author: Enzo Martellucci <[email protected]> AuthorDate: Tue Jan 13 16:26:39 2026 +0100 fix(charts): properly parse error responses in StatefulChart --- .../src/chart/components/StatefulChart.test.tsx | 92 ++++++++++++++++++++++ .../src/chart/components/StatefulChart.tsx | 11 ++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx index 35b65ac772..b02d3bb04a 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.test.tsx @@ -471,3 +471,95 @@ test('should handle chartId changes', async () => { expect(mockChartClient.loadFormData).toHaveBeenCalledTimes(2); }); }); + +test('should display error message when HTTP request fails with Response object', async () => { + const errorBody = JSON.stringify({ message: 'Error: division by zero' }); + const mockResponse = new Response(errorBody, { + status: 400, + statusText: 'Bad Request', + headers: { 'Content-Type': 'application/json' }, + }); + mockChartClient.client.post.mockRejectedValue(mockResponse); + + const onError = jest.fn(); + const { findByText } = render( + <StatefulChart + formData={mockFormData} + chartType="test_chart" + onError={onError} + />, + ); + + const errorElement = await findByText(/Error: division by zero/i); + expect(errorElement).toBeInTheDocument(); + + await waitFor(() => { + expect(onError).toHaveBeenCalledTimes(1); + expect(onError).toHaveBeenCalledWith(expect.any(Error)); + expect(onError.mock.calls[0][0].message).toBe('Error: division by zero'); + }); +}); + +test('should display error message when HTTP request fails with errors array', async () => { + const errorBody = JSON.stringify({ + errors: [ + { + message: 'Query failed: column "invalid_col" does not exist', + error_type: 'COLUMN_DOES_NOT_EXIST_ERROR', + }, + ], + }); + const mockResponse = new Response(errorBody, { + status: 422, + statusText: 'Unprocessable Entity', + headers: { 'Content-Type': 'application/json' }, + }); + mockChartClient.client.post.mockRejectedValue(mockResponse); + + const { findByText } = render( + <StatefulChart formData={mockFormData} chartType="test_chart" />, + ); + + const errorElement = await findByText( + /Query failed: column "invalid_col" does not exist/i, + ); + expect(errorElement).toBeInTheDocument(); +}); + +test('should display generic error message for network failures', async () => { + const networkError = new TypeError('Failed to fetch'); + mockChartClient.client.post.mockRejectedValue(networkError); + + const { findByText } = render( + <StatefulChart formData={mockFormData} chartType="test_chart" />, + ); + + const errorElement = await findByText(/Network error/i); + expect(errorElement).toBeInTheDocument(); +}); + +test('should pass error to custom errorComponent when provided', async () => { + const errorBody = JSON.stringify({ message: 'Custom error message' }); + const mockResponse = new Response(errorBody, { + status: 400, + statusText: 'Bad Request', + headers: { 'Content-Type': 'application/json' }, + }); + mockChartClient.client.post.mockRejectedValue(mockResponse); + + const CustomErrorComponent = ({ error }: { error: Error }) => ( + <div data-test="custom-error">Custom: {error.message}</div> + ); + + const { findByTestId } = render( + <StatefulChart + formData={mockFormData} + chartType="test_chart" + errorComponent={CustomErrorComponent} + />, + ); + + const customError = await findByTestId('custom-error'); + expect(customError).toBeInTheDocument(); + expect(customError).toHaveTextContent('Custom: Custom error message'); +}); diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx index ba22e64808..83ddb688b9 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart/components/StatefulChart.tsx @@ -25,6 +25,7 @@ import { SupersetClientInterface, buildQueryContext, RequestConfig, + getClientErrorObject, } from '../..'; import { Loading } from '../../components/Loading'; import ChartClient from '../clients/ChartClient'; @@ -279,11 +280,17 @@ export default function StatefulChart(props: StatefulChartProps) { } } catch (err) { // Ignore abort errors - if (err.name === 'AbortError') { + if ((err as Error).name === 'AbortError') { return; } - const errorObj = err as Error; + const parsedError = await getClientErrorObject( + err as Parameters<typeof getClientErrorObject>[0], + ); + const errorMessage = + parsedError.error || parsedError.message || 'An error occurred'; + + const errorObj = new Error(errorMessage); setStatus('error'); setError(errorObj);
