diff --git a/src/plugins/data/common/search/expressions/eql.ts b/src/plugins/data/common/search/expressions/eql.ts
index f82f443ea00b9..7caaa0c090466 100644
--- a/src/plugins/data/common/search/expressions/eql.ts
+++ b/src/plugins/data/common/search/expressions/eql.ts
@@ -166,7 +166,7 @@ export const getEqlFn = ({
body: response.rawResponse,
};
} catch (e) {
- request.error({ json: e });
+ request.error({ json: 'attributes' in e ? e.attributes : { message: e.message } });
throw e;
}
},
diff --git a/src/plugins/data/common/search/expressions/esdsl.ts b/src/plugins/data/common/search/expressions/esdsl.ts
index 34a67223b4be5..a18e1e3240050 100644
--- a/src/plugins/data/common/search/expressions/esdsl.ts
+++ b/src/plugins/data/common/search/expressions/esdsl.ts
@@ -188,7 +188,7 @@ export const getEsdslFn = ({
body: rawResponse,
};
} catch (e) {
- request.error({ json: e });
+ request.error({ json: 'attributes' in e ? e.attributes : { message: e.message } });
throw e;
}
},
diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts
index ba6600ba0039e..30bf10a0f1bb7 100644
--- a/src/plugins/data/common/search/expressions/esql.ts
+++ b/src/plugins/data/common/search/expressions/esql.ts
@@ -227,7 +227,9 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
.ok({ json: rawResponse });
},
error(error) {
- logInspectorRequest().error({ json: error });
+ logInspectorRequest().error({
+ json: 'attributes' in error ? error.attributes : { message: error.message },
+ });
},
})
);
diff --git a/src/plugins/data/common/search/expressions/essql.ts b/src/plugins/data/common/search/expressions/essql.ts
index a5db4674a7d14..d943b406ff7f5 100644
--- a/src/plugins/data/common/search/expressions/essql.ts
+++ b/src/plugins/data/common/search/expressions/essql.ts
@@ -248,7 +248,9 @@ export const getEssqlFn = ({ getStartDependencies }: EssqlFnArguments) => {
.ok({ json: rawResponse });
},
error(error) {
- logInspectorRequest().error({ json: error });
+ logInspectorRequest().error({
+ json: 'attributes' in error ? error.attributes : { message: error.message },
+ });
},
})
);
diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts
index b42cb7fdf4f25..4e5782a7468dd 100644
--- a/src/plugins/data/common/search/search_source/search_source.ts
+++ b/src/plugins/data/common/search/search_source/search_source.ts
@@ -458,7 +458,9 @@ export class SearchSource {
const last$ = s$
.pipe(
catchError((e) => {
- requestResponder?.error({ json: e });
+ requestResponder?.error({
+ json: 'attributes' in e ? e.attributes : { message: e.message },
+ });
return EMPTY;
}),
last(undefined, null),
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index e51bb8f208326..bc61d22c5a112 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -167,7 +167,6 @@ export type {
SerializedSearchSourceFields,
// errors
IEsError,
- Reason,
WaitUntilNextSessionCompletesOptions,
SearchResponseWarning,
SearchResponseIncompleteWarning,
diff --git a/src/plugins/data/public/search/errors/es_error.test.tsx b/src/plugins/data/public/search/errors/es_error.test.tsx
index 4d1bc8b03b8f2..1b7f57d24d4f9 100644
--- a/src/plugins/data/public/search/errors/es_error.test.tsx
+++ b/src/plugins/data/public/search/errors/es_error.test.tsx
@@ -7,6 +7,7 @@
*/
import { EsError } from './es_error';
+import { IEsError } from './types';
describe('EsError', () => {
it('contains the same body as the wrapped error', () => {
@@ -19,7 +20,7 @@ describe('EsError', () => {
reason: 'top-level reason',
},
},
- } as any;
+ } as IEsError;
const esError = new EsError(error);
expect(typeof esError.attributes).toEqual('object');
@@ -33,20 +34,22 @@ describe('EsError', () => {
'x_content_parse_exception: [x_content_parse_exception] Reason: [1:78] [date_histogram] failed to parse field [calendar_interval]',
statusCode: 400,
attributes: {
- root_cause: [
- {
- type: 'x_content_parse_exception',
- reason: '[1:78] [date_histogram] failed to parse field [calendar_interval]',
+ error: {
+ root_cause: [
+ {
+ type: 'x_content_parse_exception',
+ reason: '[1:78] [date_histogram] failed to parse field [calendar_interval]',
+ },
+ ],
+ type: 'x_content_parse_exception',
+ reason: '[1:78] [date_histogram] failed to parse field [calendar_interval]',
+ caused_by: {
+ type: 'illegal_argument_exception',
+ reason: 'The supplied interval [2q] could not be parsed as a calendar interval.',
},
- ],
- type: 'x_content_parse_exception',
- reason: '[1:78] [date_histogram] failed to parse field [calendar_interval]',
- caused_by: {
- type: 'illegal_argument_exception',
- reason: 'The supplied interval [2q] could not be parsed as a calendar interval.',
},
},
- } as any;
+ } as IEsError;
const esError = new EsError(error);
expect(esError.message).toEqual(
'EsError: The supplied interval [2q] could not be parsed as a calendar interval.'
diff --git a/src/plugins/data/public/search/errors/es_error.tsx b/src/plugins/data/public/search/errors/es_error.tsx
index a8d73baaf4d71..34d074a427187 100644
--- a/src/plugins/data/public/search/errors/es_error.tsx
+++ b/src/plugins/data/public/search/errors/es_error.tsx
@@ -8,8 +8,8 @@
import React from 'react';
import { EuiCodeBlock, EuiSpacer } from '@elastic/eui';
-import { ApplicationStart } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
+import { ApplicationStart } from '@kbn/core/public';
import { KbnError } from '@kbn/kibana-utils-plugin/common';
import { IEsError } from './types';
import { getRootCause } from './utils';
@@ -20,7 +20,7 @@ export class EsError extends KbnError {
constructor(protected readonly err: IEsError) {
super(
`EsError: ${
- getRootCause(err)?.reason ||
+ getRootCause(err?.attributes?.error)?.reason ||
i18n.translate('data.esError.unknownRootCause', { defaultMessage: 'unknown' })
}`
);
@@ -28,18 +28,20 @@ export class EsError extends KbnError {
}
public getErrorMessage(application: ApplicationStart) {
- const rootCause = getRootCause(this.err)?.reason;
- const topLevelCause = this.attributes?.reason;
+ if (!this.attributes?.error) {
+ return null;
+ }
+
+ const rootCause = getRootCause(this.attributes.error)?.reason;
+ const topLevelCause = this.attributes.error.reason;
const cause = rootCause ?? topLevelCause;
return (
<>
- {cause ? (
-
- {cause}
-
- ) : null}
+
+ {cause}
+
>
);
}
diff --git a/src/plugins/data/public/search/errors/painless_error.test.tsx b/src/plugins/data/public/search/errors/painless_error.test.tsx
index c4a540f7d21ab..4bf79a6f5b5d9 100644
--- a/src/plugins/data/public/search/errors/painless_error.test.tsx
+++ b/src/plugins/data/public/search/errors/painless_error.test.tsx
@@ -23,11 +23,13 @@ describe('PainlessError', () => {
const e = new PainlessError({
statusCode: 400,
message: 'search_phase_execution_exception',
- attributes: searchPhaseException.error,
+ attributes: {
+ error: searchPhaseException.error,
+ },
});
const component = mount(e.getErrorMessage(startMock.application));
- const failedShards = e.attributes?.failed_shards![0];
+ const failedShards = searchPhaseException.error.failed_shards![0];
const stackTraceElem = findTestSubject(component, 'painlessStackTrace').getDOMNode();
const stackTrace = failedShards!.reason.script_stack!.splice(-2).join('\n');
diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx
index 64f6c586932af..0435256f595cf 100644
--- a/src/plugins/data/public/search/errors/painless_error.tsx
+++ b/src/plugins/data/public/search/errors/painless_error.tsx
@@ -31,7 +31,7 @@ export class PainlessError extends EsError {
});
}
- const rootCause = getRootCause(this.err);
+ const rootCause = getRootCause(this.err.attributes?.error);
const scriptFromStackTrace = rootCause?.script_stack
? rootCause?.script_stack?.slice(-2).join('\n')
: undefined;
@@ -78,7 +78,7 @@ export class PainlessError extends EsError {
export function isPainlessError(err: Error | IEsError) {
if (!isEsError(err)) return false;
- const rootCause = getRootCause(err as IEsError);
+ const rootCause = getRootCause((err as IEsError).attributes?.error);
if (!rootCause) return false;
const { lang } = rootCause;
diff --git a/src/plugins/data/public/search/errors/types.ts b/src/plugins/data/public/search/errors/types.ts
index b89d784731c94..de03350a6d41c 100644
--- a/src/plugins/data/public/search/errors/types.ts
+++ b/src/plugins/data/public/search/errors/types.ts
@@ -5,39 +5,13 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
-import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
-import { KibanaServerError } from '@kbn/kibana-utils-plugin/common';
-
-export interface FailedShard {
- shard: number;
- index: string;
- node: string;
- reason: Reason;
-}
-export interface Reason {
- type: string;
- reason?: string;
- script_stack?: string[];
- position?: {
- offset: number;
- start: number;
- end: number;
- };
- lang?: estypes.ScriptLanguage;
- script?: string;
- caused_by?: {
- type: string;
- reason: string;
- };
-}
+import { estypes } from '@elastic/elasticsearch';
+import { KibanaServerError } from '@kbn/kibana-utils-plugin/common';
interface IEsErrorAttributes {
- type: string;
- reason: string;
- root_cause?: Reason[];
- failed_shards?: FailedShard[];
- caused_by?: IEsErrorAttributes;
+ rawResponse?: estypes.SearchResponseBody;
+ error?: estypes.ErrorCause;
}
export type IEsError = KibanaServerError;
diff --git a/src/plugins/data/public/search/errors/utils.ts b/src/plugins/data/public/search/errors/utils.ts
index f90a1fb461804..e1e2c51f87f3c 100644
--- a/src/plugins/data/public/search/errors/utils.ts
+++ b/src/plugins/data/public/search/errors/utils.ts
@@ -5,26 +5,21 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
-import type { ErrorCause } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
-import { KibanaServerError } from '@kbn/kibana-utils-plugin/common';
-import type { FailedShard, Reason } from './types';
-export function getFailedShards(err: KibanaServerError): FailedShard | undefined {
- const errorInfo = err.attributes;
- const failedShards = errorInfo?.failed_shards || errorInfo?.caused_by?.failed_shards;
- return failedShards ? failedShards[0] : undefined;
+import { estypes } from '@elastic/elasticsearch';
+
+function getFailedShardCause(error: estypes.ErrorCause): estypes.ErrorCause | undefined {
+ const failedShards = error.failed_shards || error.caused_by?.failed_shards;
+ return failedShards ? failedShards[0]?.reason : undefined;
}
-function getNestedCause(err: KibanaServerError | ErrorCause): Reason {
- const attr = ((err as KibanaServerError).attributes || err) as ErrorCause;
- const { type, reason, caused_by: causedBy } = attr;
- if (causedBy) {
- return getNestedCause(causedBy);
- }
- return { type, reason };
+function getNestedCause(error: estypes.ErrorCause): estypes.ErrorCause {
+ return error.caused_by ? getNestedCause(error.caused_by) : error;
}
-export function getRootCause(err: KibanaServerError) {
- // Give shard failures priority, then try to get the error navigating nested objects
- return getFailedShards(err)?.reason || getNestedCause(err);
+export function getRootCause(error?: estypes.ErrorCause): estypes.ErrorCause | undefined {
+ return error
+ ? // Give shard failures priority, then try to get the error navigating nested objects
+ getFailedShardCause(error) || getNestedCause(error)
+ : undefined;
}
diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts
index 74b4a6cda7530..013afb428931d 100644
--- a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts
+++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts
@@ -22,6 +22,7 @@ import * as resourceNotFoundException from '../../../common/search/test_data/res
import { BehaviorSubject } from 'rxjs';
import { dataPluginMock } from '../../mocks';
import { UI_SETTINGS } from '../../../common';
+import type { IEsError } from '../errors';
jest.mock('./utils', () => {
const originalModule = jest.requireActual('./utils');
@@ -151,7 +152,9 @@ describe('SearchInterceptor', () => {
new PainlessError({
statusCode: 400,
message: 'search_phase_execution_exception',
- attributes: searchPhaseException.error,
+ attributes: {
+ error: searchPhaseException.error,
+ },
})
);
expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1);
@@ -1452,10 +1455,12 @@ describe('SearchInterceptor', () => {
});
test('Should throw Painless error on server error with OSS format', async () => {
- const mockResponse: any = {
+ const mockResponse: IEsError = {
statusCode: 400,
message: 'search_phase_execution_exception',
- attributes: searchPhaseException.error,
+ attributes: {
+ error: searchPhaseException.error,
+ },
};
fetchMock.mockRejectedValueOnce(mockResponse);
const mockRequest: IEsSearchRequest = {
@@ -1466,10 +1471,12 @@ describe('SearchInterceptor', () => {
});
test('Should throw ES error on ES server error', async () => {
- const mockResponse: any = {
+ const mockResponse: IEsError = {
statusCode: 400,
message: 'resource_not_found_exception',
- attributes: resourceNotFoundException.error,
+ attributes: {
+ error: resourceNotFoundException.error,
+ },
};
fetchMock.mockRejectedValueOnce(mockResponse);
const mockRequest: IEsSearchRequest = {
diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts
index 87f2ffe97c034..24b2e1c41216a 100644
--- a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts
+++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts
@@ -194,18 +194,18 @@ export class SearchInterceptor {
// The timeout error is shown any time a request times out, or once per session, if the request is part of a session.
this.showTimeoutError(err, options?.sessionId);
return err;
- } else if (e instanceof AbortError || e instanceof BfetchRequestError) {
+ }
+
+ if (e instanceof AbortError || e instanceof BfetchRequestError) {
// In the case an application initiated abort, throw the existing AbortError, same with BfetchRequestErrors
return e;
- } else if (isEsError(e)) {
- if (isPainlessError(e)) {
- return new PainlessError(e, options?.indexPattern);
- } else {
- return new EsError(e);
- }
- } else {
- return e instanceof Error ? e : new Error(e.message);
}
+
+ if (isEsError(e)) {
+ return isPainlessError(e) ? new PainlessError(e, options?.indexPattern) : new EsError(e);
+ }
+
+ return e instanceof Error ? e : new Error(e.message);
}
private getSerializableOptions(options?: ISearchOptions) {
diff --git a/src/plugins/data/server/search/report_search_error.ts b/src/plugins/data/server/search/report_search_error.ts
new file mode 100644
index 0000000000000..dc6bf2399abf6
--- /dev/null
+++ b/src/plugins/data/server/search/report_search_error.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { errors } from '@elastic/elasticsearch';
+import { KibanaResponseFactory } from '@kbn/core/server';
+import { KbnError } from '@kbn/kibana-utils-plugin/common';
+
+// Why not use just use kibana-utils-plugin KbnServerError and reportServerError?
+//
+// Search errors need to surface additional information
+// such as rawResponse and sanitized requestParams.
+// KbnServerError and reportServerError are used widely throughtout Kibana.
+// KbnSearchError and reportSearchError exist to avoid polluting
+// non-search usages of KbnServerError and reportServerError with extra information.
+export class KbnSearchError extends KbnError {
+ public errBody?: Record;
+ constructor(message: string, public readonly statusCode: number, errBody?: Record) {
+ super(message);
+ this.errBody = errBody;
+ }
+}
+
+/**
+ * Formats any error thrown into a standardized `KbnSearchError`.
+ * @param e `Error` or `ElasticsearchClientError`
+ * @returns `KbnSearchError`
+ */
+export function getKbnSearchError(e: Error) {
+ if (e instanceof KbnSearchError) return e;
+ return new KbnSearchError(
+ e.message ?? 'Unknown error',
+ e instanceof errors.ResponseError ? e.statusCode! : 500,
+ e instanceof errors.ResponseError ? e.body : undefined
+ );
+}
+
+/**
+ *
+ * @param res Formats a `KbnSearchError` into a server error response
+ * @param err
+ */
+export function reportSearchError(res: KibanaResponseFactory, err: KbnSearchError) {
+ return res.customError({
+ statusCode: err.statusCode ?? 500,
+ body: {
+ message: err.message,
+ attributes: err.errBody
+ ? {
+ error: err.errBody.error,
+ rawResponse: err.errBody.response,
+ }
+ : undefined,
+ },
+ });
+}
diff --git a/src/plugins/data/server/search/routes/bsearch.ts b/src/plugins/data/server/search/routes/bsearch.ts
index 95b094a3793cc..bf1aa4aaa3cbc 100644
--- a/src/plugins/data/server/search/routes/bsearch.ts
+++ b/src/plugins/data/server/search/routes/bsearch.ts
@@ -48,7 +48,12 @@ export function registerBsearchRoute(
throw {
message: err.message,
statusCode: err.statusCode,
- attributes: err.errBody?.error,
+ attributes: err.errBody
+ ? {
+ error: err.errBody.error,
+ rawResponse: err.errBody.response,
+ }
+ : undefined,
};
})
)
diff --git a/src/plugins/data/server/search/routes/search.test.ts b/src/plugins/data/server/search/routes/search.test.ts
index 10b7755b2c30f..26fad8b6890e2 100644
--- a/src/plugins/data/server/search/routes/search.test.ts
+++ b/src/plugins/data/server/search/routes/search.test.ts
@@ -14,13 +14,13 @@ import { registerSearchRoute } from './search';
import { DataPluginStart } from '../../plugin';
import * as searchPhaseException from '../../../common/search/test_data/search_phase_execution_exception.json';
import * as indexNotFoundException from '../../../common/search/test_data/index_not_found_exception.json';
-import { KbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { KbnSearchError } from '../report_search_error';
describe('Search service', () => {
let mockCoreSetup: MockedKeys>;
- function mockEsError(message: string, statusCode: number, attributes?: Record) {
- return new KbnServerError(message, statusCode, attributes);
+ function mockEsError(message: string, statusCode: number, errBody?: Record) {
+ return new KbnSearchError(message, statusCode, errBody);
}
async function runMockSearch(mockContext: any, mockRequest: any, mockResponse: any) {
@@ -112,7 +112,10 @@ describe('Search service', () => {
const error: any = mockResponse.customError.mock.calls[0][0];
expect(error.statusCode).toBe(400);
expect(error.body.message).toBe('search_phase_execution_exception');
- expect(error.body.attributes).toBe(searchPhaseException.error);
+ expect(error.body.attributes).toEqual({
+ error: searchPhaseException.error,
+ rawResponse: undefined,
+ });
});
it('handler returns an error response if the search throws an index not found error', async () => {
@@ -138,7 +141,10 @@ describe('Search service', () => {
const error: any = mockResponse.customError.mock.calls[0][0];
expect(error.statusCode).toBe(404);
expect(error.body.message).toBe('index_not_found_exception');
- expect(error.body.attributes).toBe(indexNotFoundException.error);
+ expect(error.body.attributes).toEqual({
+ error: indexNotFoundException.error,
+ rawResponse: undefined,
+ });
});
it('handler returns an error response if the search throws a general error', async () => {
diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts
index 9d338095f25c0..8b302a81aea1a 100644
--- a/src/plugins/data/server/search/routes/search.ts
+++ b/src/plugins/data/server/search/routes/search.ts
@@ -9,6 +9,7 @@
import { first } from 'rxjs/operators';
import { schema } from '@kbn/config-schema';
import { reportServerError } from '@kbn/kibana-utils-plugin/server';
+import { reportSearchError } from '../report_search_error';
import { getRequestAbortedSignal } from '../../lib';
import type { DataPluginRouter } from '../types';
@@ -71,7 +72,7 @@ export function registerSearchRoute(router: DataPluginRouter): void {
return res.ok({ body: response });
} catch (err) {
- return reportServerError(res, err);
+ return reportSearchError(res, err);
}
}
);
diff --git a/src/plugins/data/server/search/strategies/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/strategies/es_search/es_search_strategy.test.ts
index 15a6a4df7eed8..9db6b00200bcc 100644
--- a/src/plugins/data/server/search/strategies/es_search/es_search_strategy.test.ts
+++ b/src/plugins/data/server/search/strategies/es_search/es_search_strategy.test.ts
@@ -14,7 +14,7 @@ import { SearchStrategyDependencies } from '../../types';
import * as indexNotFoundException from '../../../../common/search/test_data/index_not_found_exception.json';
import { errors } from '@elastic/elasticsearch';
-import { KbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { KbnSearchError } from '../../report_search_error';
import { firstValueFrom } from 'rxjs';
describe('ES search strategy', () => {
@@ -150,7 +150,7 @@ describe('ES search strategy', () => {
.toPromise();
} catch (e) {
expect(esClient.search).toBeCalled();
- expect(e).toBeInstanceOf(KbnServerError);
+ expect(e).toBeInstanceOf(KbnSearchError);
expect(e.statusCode).toBe(404);
expect(e.message).toBe(errResponse.message);
expect(e.errBody).toBe(indexNotFoundException);
@@ -167,7 +167,7 @@ describe('ES search strategy', () => {
.toPromise();
} catch (e) {
expect(esClient.search).toBeCalled();
- expect(e).toBeInstanceOf(KbnServerError);
+ expect(e).toBeInstanceOf(KbnSearchError);
expect(e.statusCode).toBe(500);
expect(e.message).toBe(errResponse.message);
expect(e.errBody).toBe(undefined);
@@ -184,14 +184,14 @@ describe('ES search strategy', () => {
.toPromise();
} catch (e) {
expect(esClient.search).toBeCalled();
- expect(e).toBeInstanceOf(KbnServerError);
+ expect(e).toBeInstanceOf(KbnSearchError);
expect(e.statusCode).toBe(500);
expect(e.message).toBe(errResponse.message);
expect(e.errBody).toBe(undefined);
}
});
- it('throws KbnServerError for unknown index type', async () => {
+ it('throws KbnSearchError for unknown index type', async () => {
const params = { index: 'logstash-*', ignore_unavailable: false, timeout: '1000ms' };
try {
@@ -200,7 +200,7 @@ describe('ES search strategy', () => {
.toPromise();
} catch (e) {
expect(esClient.search).not.toBeCalled();
- expect(e).toBeInstanceOf(KbnServerError);
+ expect(e).toBeInstanceOf(KbnSearchError);
expect(e.message).toBe('Unsupported index pattern type banana');
expect(e.statusCode).toBe(400);
expect(e.errBody).toBe(undefined);
diff --git a/src/plugins/data/server/search/strategies/es_search/es_search_strategy.ts b/src/plugins/data/server/search/strategies/es_search/es_search_strategy.ts
index b2aed5804f248..64b2234a573c8 100644
--- a/src/plugins/data/server/search/strategies/es_search/es_search_strategy.ts
+++ b/src/plugins/data/server/search/strategies/es_search/es_search_strategy.ts
@@ -9,7 +9,7 @@
import { firstValueFrom, from, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import type { Logger, SharedGlobalConfig } from '@kbn/core/server';
-import { getKbnServerError, KbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { getKbnSearchError, KbnSearchError } from '../../report_search_error';
import type { ISearchStrategy } from '../../types';
import type { SearchUsage } from '../../collectors/search';
import { getDefaultSearchParams, getShardTimeout } from './request_utils';
@@ -25,14 +25,14 @@ export const esSearchStrategyProvider = (
* @param request
* @param options
* @param deps
- * @throws `KbnServerError`
+ * @throws `KbnSearchError`
* @returns `Observable>`
*/
search: (request, { abortSignal, transport, ...options }, { esClient, uiSettingsClient }) => {
// Only default index pattern type is supported here.
// See ese for other type support.
if (request.indexType) {
- throw new KbnServerError(`Unsupported index pattern type ${request.indexType}`, 400);
+ throw new KbnSearchError(`Unsupported index pattern type ${request.indexType}`, 400);
}
const isPit = request.params?.body?.pit != null;
@@ -57,7 +57,7 @@ export const esSearchStrategyProvider = (
const response = shimHitsTotal(body, options);
return toKibanaSearchResponse(response);
} catch (e) {
- throw getKbnServerError(e);
+ throw getKbnSearchError(e);
}
};
diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts
index 96e401204978f..6c1746984c86b 100644
--- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts
+++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.test.ts
@@ -8,6 +8,7 @@
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { KbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { KbnSearchError } from '../../report_search_error';
import { errors } from '@elastic/elasticsearch';
import * as indexNotFoundException from '../../../../common/search/test_data/index_not_found_exception.json';
import * as xContentParseException from '../../../../common/search/test_data/x_content_parse_exception.json';
@@ -456,14 +457,14 @@ describe('ES search strategy', () => {
mockLogger
);
- let err: KbnServerError | undefined;
+ let err: KbnSearchError | undefined;
try {
await esSearch.search({ params }, {}, mockDeps).toPromise();
} catch (e) {
err = e;
}
expect(mockSubmitCaller).toBeCalled();
- expect(err).toBeInstanceOf(KbnServerError);
+ expect(err).toBeInstanceOf(KbnSearchError);
expect(err?.statusCode).toBe(404);
expect(err?.message).toBe(errResponse.message);
expect(err?.errBody).toBe(indexNotFoundException);
@@ -481,14 +482,14 @@ describe('ES search strategy', () => {
mockLogger
);
- let err: KbnServerError | undefined;
+ let err: KbnSearchError | undefined;
try {
await esSearch.search({ params }, {}, mockDeps).toPromise();
} catch (e) {
err = e;
}
expect(mockSubmitCaller).toBeCalled();
- expect(err).toBeInstanceOf(KbnServerError);
+ expect(err).toBeInstanceOf(KbnSearchError);
expect(err?.statusCode).toBe(500);
expect(err?.message).toBe(errResponse.message);
expect(err?.errBody).toBe(undefined);
diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts
index 88d1606935562..460d8f95ed3e4 100644
--- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts
+++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts
@@ -11,7 +11,8 @@ import type { IScopedClusterClient, Logger, SharedGlobalConfig } from '@kbn/core
import { catchError, tap } from 'rxjs/operators';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { firstValueFrom, from } from 'rxjs';
-import { getKbnServerError, KbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { getKbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { getKbnSearchError, KbnSearchError } from '../../report_search_error';
import type { ISearchStrategy, SearchStrategyDependencies } from '../../types';
import type {
IAsyncSearchOptions,
@@ -94,7 +95,7 @@ export const enhancedEsSearchStrategyProvider = (
tap((response) => (id = response.id)),
tap(searchUsageObserver(logger, usage)),
catchError((e) => {
- throw getKbnServerError(e);
+ throw getKbnSearchError(e);
})
);
}
@@ -136,7 +137,7 @@ export const enhancedEsSearchStrategyProvider = (
...getTotalLoaded(response),
};
} catch (e) {
- throw getKbnServerError(e);
+ throw getKbnSearchError(e);
}
}
@@ -146,12 +147,12 @@ export const enhancedEsSearchStrategyProvider = (
* @param options
* @param deps `SearchStrategyDependencies`
* @returns `Observable>`
- * @throws `KbnServerError`
+ * @throws `KbnSearchError`
*/
search: (request, options: IAsyncSearchOptions, deps) => {
logger.debug(`search ${JSON.stringify(request.params) || request.id}`);
if (request.indexType && request.indexType !== 'rollup') {
- throw new KbnServerError('Unknown indexType', 400);
+ throw new KbnSearchError('Unknown indexType', 400);
}
if (request.indexType === undefined || !deps.rollupsEnabled) {
diff --git a/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts b/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts
index 2af032826189f..460755a74df8f 100644
--- a/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts
+++ b/src/plugins/data/server/search/strategies/esql_search/esql_search_strategy.ts
@@ -8,7 +8,7 @@
import { from } from 'rxjs';
import type { Logger } from '@kbn/core/server';
-import { getKbnServerError, KbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { getKbnSearchError, KbnSearchError } from '../../report_search_error';
import type { ISearchStrategy } from '../../types';
const ES_TIMEOUT_IN_MS = 120000;
@@ -21,7 +21,7 @@ export const esqlSearchStrategyProvider = (
* @param request
* @param options
* @param deps
- * @throws `KbnServerError`
+ * @throws `KbnSearchError`
* @returns `Observable>`
*/
search: (request, { abortSignal, ...options }, { esClient, uiSettingsClient }) => {
@@ -39,7 +39,7 @@ export const esqlSearchStrategyProvider = (
// Only default index pattern type is supported here.
// See ese for other type support.
if (request.indexType) {
- throw new KbnServerError(`Unsupported index pattern type ${request.indexType}`, 400);
+ throw new KbnSearchError(`Unsupported index pattern type ${request.indexType}`, 400);
}
const search = async () => {
@@ -67,7 +67,7 @@ export const esqlSearchStrategyProvider = (
warning: headers?.warning,
};
} catch (e) {
- throw getKbnServerError(e);
+ throw getKbnSearchError(e);
}
};
diff --git a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts
index 530ee16ae75b9..700c658de10c0 100644
--- a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts
+++ b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts
@@ -7,7 +7,7 @@
*/
import { merge } from 'lodash';
-import { KbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { KbnSearchError } from '../../report_search_error';
import { errors } from '@elastic/elasticsearch';
import * as indexNotFoundException from '../../../../common/search/test_data/index_not_found_exception.json';
import { SearchStrategyDependencies } from '../../types';
@@ -221,14 +221,14 @@ describe('SQL search strategy', () => {
};
const esSearch = await sqlSearchStrategyProvider(mockSearchConfig, mockLogger);
- let err: KbnServerError | undefined;
+ let err: KbnSearchError | undefined;
try {
await esSearch.search({ params }, {}, mockDeps).toPromise();
} catch (e) {
err = e;
}
expect(mockSqlQuery).toBeCalled();
- expect(err).toBeInstanceOf(KbnServerError);
+ expect(err).toBeInstanceOf(KbnSearchError);
expect(err?.statusCode).toBe(404);
expect(err?.message).toBe(errResponse.message);
expect(err?.errBody).toBe(indexNotFoundException);
@@ -245,14 +245,14 @@ describe('SQL search strategy', () => {
};
const esSearch = await sqlSearchStrategyProvider(mockSearchConfig, mockLogger);
- let err: KbnServerError | undefined;
+ let err: KbnSearchError | undefined;
try {
await esSearch.search({ params }, {}, mockDeps).toPromise();
} catch (e) {
err = e;
}
expect(mockSqlQuery).toBeCalled();
- expect(err).toBeInstanceOf(KbnServerError);
+ expect(err).toBeInstanceOf(KbnSearchError);
expect(err?.statusCode).toBe(500);
expect(err?.message).toBe(errResponse.message);
expect(err?.errBody).toBe(undefined);
diff --git a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts
index d5d1eafc5c214..34134a1491cd0 100644
--- a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts
+++ b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts
@@ -11,6 +11,7 @@ import type { IScopedClusterClient, Logger } from '@kbn/core/server';
import { catchError, tap } from 'rxjs/operators';
import { SqlQueryResponse } from '@elastic/elasticsearch/lib/api/types';
import { getKbnServerError } from '@kbn/kibana-utils-plugin/server';
+import { getKbnSearchError } from '../../report_search_error';
import type { ISearchStrategy, SearchStrategyDependencies } from '../../types';
import type {
IAsyncSearchOptions,
@@ -94,7 +95,7 @@ export const sqlSearchStrategyProvider = (
}).pipe(
tap((response) => (id = response.id)),
catchError((e) => {
- throw getKbnServerError(e);
+ throw getKbnSearchError(e);
})
);
}
@@ -105,7 +106,7 @@ export const sqlSearchStrategyProvider = (
* @param options
* @param deps `SearchStrategyDependencies`
* @returns `Observable>`
- * @throws `KbnServerError`
+ * @throws `KbnSearchError`
*/
search: (request, options: IAsyncSearchOptions, deps) => {
logger.debug(`sql search: search request=${JSON.stringify(request)}`);
diff --git a/test/api_integration/apis/search/verify_error.ts b/test/api_integration/apis/search/verify_error.ts
index 1973fe4e4ab36..b7a38f1117fe2 100644
--- a/test/api_integration/apis/search/verify_error.ts
+++ b/test/api_integration/apis/search/verify_error.ts
@@ -20,7 +20,8 @@ export const verifyErrorResponse = (
}
if (shouldHaveAttrs) {
expect(r).to.have.property('attributes');
- expect(r.attributes).to.have.property('root_cause');
+ expect(r.attributes).to.have.property('error');
+ expect(r.attributes.error).to.have.property('root_cause');
} else {
expect(r).not.to.have.property('attributes');
}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts b/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts
index fe5c1f85f1e92..7e3b696a8e75f 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts
+++ b/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts
@@ -21,6 +21,45 @@ const runtimeFieldError = {
message: 'status_exception',
statusCode: 400,
attributes: {
+ error: {
+ type: 'status_exception',
+ reason: 'error while executing search',
+ caused_by: {
+ type: 'search_phase_execution_exception',
+ reason: 'all shards failed',
+ phase: 'query',
+ grouped: true,
+ failed_shards: [
+ {
+ shard: 0,
+ index: 'indexpattern_source',
+ node: 'jtqB1-UhQluyjeXIpQFqAA',
+ reason: {
+ type: 'script_exception',
+ reason: 'runtime error',
+ script_stack: [
+ 'java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)',
+ 'java.base/java.lang.Integer.parseInt(Integer.java:652)',
+ 'java.base/java.lang.Integer.parseInt(Integer.java:770)',
+ "emit(Integer.parseInt('hello'))",
+ ' ^---- HERE',
+ ],
+ script: "emit(Integer.parseInt('hello'))",
+ lang: 'painless',
+ position: { offset: 12, start: 0, end: 31 },
+ caused_by: {
+ type: 'number_format_exception',
+ reason: 'For input string: "hello"',
+ },
+ },
+ },
+ ],
+ },
+ },
+ },
+ },
+ attributes: {
+ error: {
type: 'status_exception',
reason: 'error while executing search',
caused_by: {
@@ -53,38 +92,6 @@ const runtimeFieldError = {
},
},
},
- attributes: {
- type: 'status_exception',
- reason: 'error while executing search',
- caused_by: {
- type: 'search_phase_execution_exception',
- reason: 'all shards failed',
- phase: 'query',
- grouped: true,
- failed_shards: [
- {
- shard: 0,
- index: 'indexpattern_source',
- node: 'jtqB1-UhQluyjeXIpQFqAA',
- reason: {
- type: 'script_exception',
- reason: 'runtime error',
- script_stack: [
- 'java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)',
- 'java.base/java.lang.Integer.parseInt(Integer.java:652)',
- 'java.base/java.lang.Integer.parseInt(Integer.java:770)',
- "emit(Integer.parseInt('hello'))",
- ' ^---- HERE',
- ],
- script: "emit(Integer.parseInt('hello'))",
- lang: 'painless',
- position: { offset: 12, start: 0, end: 31 },
- caused_by: { type: 'number_format_exception', reason: 'For input string: "hello"' },
- },
- },
- ],
- },
- },
},
};
@@ -99,6 +106,31 @@ const scriptedFieldError = {
message: 'status_exception',
statusCode: 500,
attributes: {
+ error: {
+ type: 'status_exception',
+ reason: 'error while executing search',
+ caused_by: {
+ type: 'search_phase_execution_exception',
+ reason: 'all shards failed',
+ phase: 'query',
+ grouped: true,
+ failed_shards: [
+ {
+ shard: 0,
+ index: 'indexpattern_source',
+ node: 'jtqB1-UhQluyjeXIpQFqAA',
+ reason: {
+ type: 'aggregation_execution_exception',
+ reason: 'Unsupported script value [hello], expected a number, date, or boolean',
+ },
+ },
+ ],
+ },
+ },
+ },
+ },
+ attributes: {
+ error: {
type: 'status_exception',
reason: 'error while executing search',
caused_by: {
@@ -120,27 +152,6 @@ const scriptedFieldError = {
},
},
},
- attributes: {
- type: 'status_exception',
- reason: 'error while executing search',
- caused_by: {
- type: 'search_phase_execution_exception',
- reason: 'all shards failed',
- phase: 'query',
- grouped: true,
- failed_shards: [
- {
- shard: 0,
- index: 'indexpattern_source',
- node: 'jtqB1-UhQluyjeXIpQFqAA',
- reason: {
- type: 'aggregation_execution_exception',
- reason: 'Unsupported script value [hello], expected a number, date, or boolean',
- },
- },
- ],
- },
- },
},
};
@@ -174,41 +185,7 @@ const tsdbCounterUsedWithWrongOperationError = {
name: 'Error',
original: {
attributes: {
- type: 'status_exception',
- reason: 'error while executing search',
- caused_by: {
- type: 'search_phase_execution_exception',
- reason: 'all shards failed',
- phase: 'query',
- grouped: true,
- failed_shards: [
- {
- shard: 0,
- index: 'tsdb_index',
- reason: {
- type: 'illegal_argument_exception',
- reason:
- 'Field [bytes_counter] of type [long][counter] is not supported for aggregation [sum]',
- },
- },
- ],
- caused_by: {
- type: 'illegal_argument_exception',
- reason:
- 'Field [bytes_counter] of type [long][counter] is not supported for aggregation [sum]',
- caused_by: {
- type: 'illegal_argument_exception',
- reason:
- 'Field [bytes_counter] of type [long][counter] is not supported for aggregation [sum]',
- },
- },
- },
- },
- err: {
- message:
- 'status_exception\n\tCaused by:\n\t\tsearch_phase_execution_exception: all shards failed',
- statusCode: 400,
- attributes: {
+ error: {
type: 'status_exception',
reason: 'error while executing search',
caused_by: {
@@ -240,6 +217,44 @@ const tsdbCounterUsedWithWrongOperationError = {
},
},
},
+ err: {
+ message:
+ 'status_exception\n\tCaused by:\n\t\tsearch_phase_execution_exception: all shards failed',
+ statusCode: 400,
+ attributes: {
+ error: {
+ type: 'status_exception',
+ reason: 'error while executing search',
+ caused_by: {
+ type: 'search_phase_execution_exception',
+ reason: 'all shards failed',
+ phase: 'query',
+ grouped: true,
+ failed_shards: [
+ {
+ shard: 0,
+ index: 'tsdb_index',
+ reason: {
+ type: 'illegal_argument_exception',
+ reason:
+ 'Field [bytes_counter] of type [long][counter] is not supported for aggregation [sum]',
+ },
+ },
+ ],
+ caused_by: {
+ type: 'illegal_argument_exception',
+ reason:
+ 'Field [bytes_counter] of type [long][counter] is not supported for aggregation [sum]',
+ caused_by: {
+ type: 'illegal_argument_exception',
+ reason:
+ 'Field [bytes_counter] of type [long][counter] is not supported for aggregation [sum]',
+ },
+ },
+ },
+ },
+ },
+ },
},
};
diff --git a/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx b/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx
index fd98a0654983e..c336101033eae 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx
@@ -7,18 +7,16 @@
import { i18n } from '@kbn/i18n';
import { isEqual, uniqWith } from 'lodash';
+import { estypes } from '@elastic/elasticsearch';
import { ExpressionRenderError } from '@kbn/expressions-plugin/public';
import type { CoreStart } from '@kbn/core/public';
import { isEsError } from '@kbn/data-plugin/public';
-import type { IEsError, Reason } from '@kbn/data-plugin/public';
import React from 'react';
import { EuiLink } from '@elastic/eui';
import { RemovableUserMessage } from '../types';
-type ErrorCause = Required['attributes'];
-
interface RequestError extends Error {
- body?: { attributes?: { error: { caused_by: ErrorCause } } };
+ body?: { attributes?: { error: { caused_by: estypes.ErrorCause } } };
}
interface ReasonDescription {
@@ -62,7 +60,7 @@ function getNestedErrorClauseWithContext({
caused_by: causedBy,
lang,
script,
-}: Reason): ReasonDescription[] {
+}: estypes.ErrorCause): ReasonDescription[] {
if (!causedBy) {
// scripted fields error has changed with no particular hint about painless in it,
// so it tries to lookup in the message for the script word
@@ -83,12 +81,12 @@ function getNestedErrorClauseWithContext({
return [{ ...payload, context: { type, reason } }];
}
-function getNestedErrorClause(e: ErrorCause | Reason): ReasonDescription[] {
+function getNestedErrorClause(e: estypes.ErrorCause): ReasonDescription[] {
const { type, reason = '', caused_by: causedBy } = e;
// Painless scripts errors are nested within the failed_shards property
if ('failed_shards' in e) {
if (e.failed_shards) {
- return e.failed_shards.flatMap((shardCause) =>
+ return (e.failed_shards as estypes.ShardFailure[]).flatMap((shardCause) =>
getNestedErrorClauseWithContext(shardCause.reason)
);
}
@@ -101,13 +99,15 @@ function getNestedErrorClause(e: ErrorCause | Reason): ReasonDescription[] {
function getErrorSources(e: Error) {
if (isRequestError(e)) {
- return getNestedErrorClause(e.body!.attributes!.error as ErrorCause);
+ return getNestedErrorClause(e.body!.attributes!.error as estypes.ErrorCause);
}
if (isEsError(e)) {
- if (e.attributes?.reason) {
- return getNestedErrorClause(e.attributes);
+ if (e.attributes?.error?.reason) {
+ return getNestedErrorClause(e.attributes.error);
+ }
+ if (e.attributes?.error?.caused_by) {
+ return getNestedErrorClause(e.attributes.error.caused_by);
}
- return getNestedErrorClause(e.attributes?.caused_by as ErrorCause);
}
return [];
}
diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts
index 250c000575d07..ad3334e536793 100644
--- a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts
+++ b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts
@@ -234,7 +234,7 @@ describe('useAppToasts', () => {
it('prefers the attributes reason if we have it for the message', async () => {
const error: IEsError = {
- attributes: { type: 'some type', reason: 'message we want' },
+ attributes: { error: { type: 'some type', reason: 'message we want' } },
statusCode: 200,
message: 'message we do not want',
};
@@ -244,11 +244,11 @@ describe('useAppToasts', () => {
it('works with an EsError, by using the inner error and not outer error if available', async () => {
const error: MaybeESError = {
- attributes: { type: 'some type', reason: 'message we want' },
+ attributes: { error: { type: 'some type', reason: 'message we want' } },
statusCode: 400,
err: {
statusCode: 200,
- attributes: { reason: 'attribute message we do not want' },
+ attributes: { error: { reason: 'attribute message we do not want' } },
},
message: 'main message we do not want',
};
@@ -258,11 +258,11 @@ describe('useAppToasts', () => {
it('creates a stack trace of a EsError and not the outer object', async () => {
const error: MaybeESError = {
- attributes: { type: 'some type', reason: 'message we do not want' },
+ attributes: { error: { type: 'some type', reason: 'message we do not want' } },
statusCode: 400,
err: {
statusCode: 200,
- attributes: { reason: 'attribute message we do want' },
+ attributes: { error: { reason: 'attribute message we do want' } },
},
message: 'main message we do not want',
};
@@ -270,7 +270,7 @@ describe('useAppToasts', () => {
const parsedStack = JSON.parse(result.stack ?? '');
expect(parsedStack).toEqual({
statusCode: 200,
- attributes: { reason: 'attribute message we do want' },
+ attributes: { error: { reason: 'attribute message we do want' } },
});
});
});
diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts
index 3c4ad68b221ea..99500a42b8c35 100644
--- a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts
+++ b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts
@@ -98,8 +98,10 @@ export const esErrorToErrorStack = (error: IEsError & MaybeESError): Error => {
? `(${error.statusCode})`
: '';
const stringifiedError = getStringifiedStack(maybeUnWrapped);
- const adaptedError = new Error(`${error.attributes?.reason ?? error.message} ${statusCode}`);
- adaptedError.name = error.attributes?.reason ?? error.message;
+ const adaptedError = new Error(
+ `${error.attributes?.error?.reason ?? error.message} ${statusCode}`
+ );
+ adaptedError.name = error.attributes?.error?.reason ?? error.message;
if (stringifiedError != null) {
adaptedError.stack = stringifiedError;
}
diff --git a/x-pack/plugins/timelines/public/hooks/use_app_toasts.ts b/x-pack/plugins/timelines/public/hooks/use_app_toasts.ts
index 4a3cc315ae693..a33ee86e0a31a 100644
--- a/x-pack/plugins/timelines/public/hooks/use_app_toasts.ts
+++ b/x-pack/plugins/timelines/public/hooks/use_app_toasts.ts
@@ -93,8 +93,10 @@ export const esErrorToErrorStack = (error: IEsError & MaybeESError): Error => {
? `(${error.statusCode})`
: '';
const stringifiedError = getStringifiedStack(maybeUnWrapped);
- const adaptedError = new Error(`${error.attributes?.reason ?? error.message} ${statusCode}`);
- adaptedError.name = error.attributes?.reason ?? error.message;
+ const adaptedError = new Error(
+ `${error.attributes?.error?.reason ?? error.message} ${statusCode}`
+ );
+ adaptedError.name = error.attributes?.error?.reason ?? error.message;
if (stringifiedError != null) {
adaptedError.stack = stringifiedError;
}
diff --git a/x-pack/test/api_integration/apis/search/search.ts b/x-pack/test/api_integration/apis/search/search.ts
index 48ff19e51623d..391923601d7c5 100644
--- a/x-pack/test/api_integration/apis/search/search.ts
+++ b/x-pack/test/api_integration/apis/search/search.ts
@@ -411,7 +411,11 @@ export default function ({ getService }: FtrProviderContext) {
.set('kbn-xsrf', 'foo')
.send()
.expect(400);
- verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true);
+
+ expect(resp.body.statusCode).to.be(400);
+ expect(resp.body.message).to.include.string('illegal_argument_exception');
+ expect(resp.body).to.have.property('attributes');
+ expect(resp.body.attributes).to.have.property('root_cause');
});
it('should delete an in-progress search', async function () {
diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_oss/verify_error.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/verify_error.ts
index 3523cefc8b33f..be1ceaf1d535e 100644
--- a/x-pack/test_serverless/api_integration/test_suites/common/search_oss/verify_error.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/common/search_oss/verify_error.ts
@@ -19,7 +19,8 @@ export const verifyErrorResponse = (
}
if (shouldHaveAttrs) {
expect(r).to.have.property('attributes');
- expect(r.attributes).to.have.property('root_cause');
+ expect(r.attributes).to.have.property('error');
+ expect(r.attributes.error).to.have.property('root_cause');
} else {
expect(r).not.to.have.property('attributes');
}
diff --git a/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts
index d820f22e72567..ecced2db57ee9 100644
--- a/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/common/search_xpack/search.ts
@@ -380,7 +380,10 @@ export default function ({ getService }: FtrProviderContext) {
.set('kbn-xsrf', 'foo')
.send()
.expect(400);
- verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true);
+ expect(resp.body.statusCode).to.be(400);
+ expect(resp.body.message).to.include.string('illegal_argument_exception');
+ expect(resp.body).to.have.property('attributes');
+ expect(resp.body.attributes).to.have.property('root_cause');
});
it('should delete an in-progress search', async function () {