{state.isEmpty &&
}
{state.isLoading &&
}
- {!state.isLoading && state.error && renderError && renderError(state.error.message)}
+ {!state.isLoading &&
+ state.error &&
+ renderError &&
+ renderError(state.error.message, state.error)}
{
+ renderError={(errorMessage?: string | null, error?: ExpressionRenderError | null) => {
+ const visibleErrorMessage = getOriginalRequestErrorMessage(error) || errorMessage;
return (
@@ -354,7 +357,7 @@ export const InnerVisualizationWrapper = ({
defaultMessage="An error occurred when loading data."
/>
- {errorMessage ? (
+ {visibleErrorMessage ? (
{
@@ -369,7 +372,7 @@ export const InnerVisualizationWrapper = ({
})}
- {localState.expandError ? errorMessage : null}
+ {localState.expandError ? visibleErrorMessage : null}
) : null}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx
index d0d2360ddc10..4fb0630a305e 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx
@@ -13,6 +13,7 @@ import {
ReactExpressionRendererType,
} from 'src/plugins/expressions/public';
import { ExecutionContextSearch } from 'src/plugins/expressions';
+import { getOriginalRequestErrorMessage } from '../error_helper';
export interface ExpressionWrapperProps {
ExpressionRenderer: ReactExpressionRendererType;
@@ -50,7 +51,20 @@ export function ExpressionWrapper({
padding="m"
expression={expression}
searchContext={searchContext}
- renderError={(error) =>
{error}
}
+ renderError={(errorMessage, error) => (
+
+
+
+
+
+
+
+ {getOriginalRequestErrorMessage(error) || errorMessage}
+
+
+
+
+ )}
onEvent={handleEvent}
/>
diff --git a/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts b/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts
new file mode 100644
index 000000000000..79faa5a47def
--- /dev/null
+++ b/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import { ExpressionRenderError } from 'src/plugins/expressions/public';
+
+interface ElasticsearchErrorClause {
+ type: string;
+ reason: string;
+ caused_by?: ElasticsearchErrorClause;
+}
+
+interface RequestError extends Error {
+ body?: { attributes?: { error: ElasticsearchErrorClause } };
+}
+
+const isRequestError = (e: Error | RequestError): e is RequestError => {
+ if ('body' in e) {
+ return e.body?.attributes?.error?.caused_by !== undefined;
+ }
+ return false;
+};
+
+function getNestedErrorClause({
+ type,
+ reason,
+ caused_by: causedBy,
+}: ElasticsearchErrorClause): { type: string; reason: string } {
+ if (causedBy) {
+ return getNestedErrorClause(causedBy);
+ }
+ return { type, reason };
+}
+
+export function getOriginalRequestErrorMessage(error?: ExpressionRenderError | null) {
+ if (error && 'original' in error && error.original && isRequestError(error.original)) {
+ const rootError = getNestedErrorClause(error.original.body!.attributes!.error);
+ if (rootError.reason && rootError.type) {
+ return i18n.translate('xpack.lens.editorFrame.expressionFailureMessage', {
+ defaultMessage: 'Request error: {type}, {reason}',
+ values: {
+ reason: rootError.reason,
+ type: rootError.type,
+ },
+ });
+ }
+ }
+}