Compare commits

...

1 Commits

Author SHA1 Message Date
sadpandajoe
3b73f3dfbf fix(sql-lab): dedupe doubled error message on query failure
Async query failures populate both query.extra.errors (from the poll
payload merged by REFRESH_QUERIES) and query.errors (from the queryFailed
action dispatched by QueryAutoRefresh with query.extra?.errors). ResultSet
concatenated both arrays, rendering the identical error twice.

Dedupe the concatenated errors with lodash uniqWith(isEqual) so exactly one
error message is shown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 23:58:32 +00:00
2 changed files with 43 additions and 2 deletions

View File

@@ -355,6 +355,41 @@ describe('ResultSet', () => {
expect(errorMessages).toHaveLength(errors.length);
});
test('should not duplicate errors present in both errors and extra.errors', async () => {
// Async query failures populate `extra.errors` (from the poll payload) and
// `errors` (from the queryFailed action) with the same error object; the
// failure message should still be rendered only once.
const error = {
message: 'Something went wrong',
error_type: 'TEST_ERROR',
level: 'error',
extra: null,
};
const duplicatedErrorQuery = {
...failedQueryWithErrors,
errors: [error],
extra: { errors: [error] },
};
const duplicatedErrorState = {
...initialState,
sqlLab: {
...initialState.sqlLab,
queries: {
[duplicatedErrorQuery.id]: duplicatedErrorQuery,
},
},
};
await waitFor(() => {
setup(
{ ...mockedProps, queryId: duplicatedErrorQuery.id },
mockStore(duplicatedErrorState),
);
});
const errorMessages = screen.getAllByTestId('error-message');
expect(errorMessages).toHaveLength(1);
});
test('should render a timeout error with a retrial button', async () => {
await waitFor(() => {
setup(

View File

@@ -30,7 +30,7 @@ import AutoSizer from 'react-virtualized-auto-sizer';
import { shallowEqual, useSelector } from 'react-redux';
import { useAppDispatch } from 'src/SqlLab/hooks/useAppDispatch';
import { useHistory } from 'react-router-dom';
import { pick } from 'lodash-es';
import { isEqual, pick, uniqWith } from 'lodash-es';
import {
Button,
ButtonGroup,
@@ -618,7 +618,13 @@ const ResultSet = ({
}
if (query.state === QueryState.Failed) {
const errors = [...(query.extra?.errors || []), ...(query.errors || [])];
// `query.extra.errors` (from the async poll payload) and `query.errors`
// (from the queryFailed action) can hold the same error, so dedupe to
// avoid rendering the failure message twice.
const errors = uniqWith(
[...(query.extra?.errors || []), ...(query.errors || [])],
isEqual,
);
return (
<ResultlessStyles>