diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfoundesunavailableerror.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfoundesunavailableerror.md
new file mode 100644
index 0000000000000..e05f9466aa9ee
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfoundesunavailableerror.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) > [createGenericNotFoundEsUnavailableError](./kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfoundesunavailableerror.md)
+
+## SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError() method
+
+Signature:
+
+```typescript
+static createGenericNotFoundEsUnavailableError(type: string, id: string): DecoratedError;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| type | string
| |
+| id | string
| |
+
+Returns:
+
+`DecoratedError`
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.md
index 2dc78f2df3a83..67056c8a3cb50 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.md
@@ -18,6 +18,7 @@ export declare class SavedObjectsErrorHelpers
| [createBadRequestError(reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.createbadrequesterror.md) | static
| |
| [createConflictError(type, id, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.createconflicterror.md) | static
| |
| [createGenericNotFoundError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfounderror.md) | static
| |
+| [createGenericNotFoundEsUnavailableError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfoundesunavailableerror.md) | static
| |
| [createIndexAliasNotFoundError(alias)](./kibana-plugin-core-server.savedobjectserrorhelpers.createindexaliasnotfounderror.md) | static
| |
| [createInvalidVersionError(versionInput)](./kibana-plugin-core-server.savedobjectserrorhelpers.createinvalidversionerror.md) | static
| |
| [createTooManyRequestsError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.createtoomanyrequestserror.md) | static
| |
diff --git a/src/core/server/elasticsearch/client/mocks.ts b/src/core/server/elasticsearch/client/mocks.ts
index a7fbce7180223..848d9c204bfbf 100644
--- a/src/core/server/elasticsearch/client/mocks.ts
+++ b/src/core/server/elasticsearch/client/mocks.ts
@@ -141,9 +141,10 @@ export type MockedTransportRequestPromise = TransportRequestPromise & {
const createSuccessTransportRequestPromise = (
body: T,
- { statusCode = 200 }: { statusCode?: number } = {}
+ { statusCode = 200 }: { statusCode?: number } = {},
+ headers?: Record
): MockedTransportRequestPromise> => {
- const response = createApiResponse({ body, statusCode });
+ const response = createApiResponse({ body, statusCode, headers });
const promise = Promise.resolve(response);
(promise as MockedTransportRequestPromise>).abort = jest.fn();
diff --git a/src/core/server/elasticsearch/index.ts b/src/core/server/elasticsearch/index.ts
index d97e3331c7cf5..8bcc841669fc9 100644
--- a/src/core/server/elasticsearch/index.ts
+++ b/src/core/server/elasticsearch/index.ts
@@ -37,3 +37,4 @@ export type {
GetResponse,
DeleteDocumentResponse,
} from './client';
+export { isSupportedEsServer } from './supported_server_response_check';
diff --git a/src/core/server/elasticsearch/supported_server_response_check.ts b/src/core/server/elasticsearch/supported_server_response_check.ts
new file mode 100644
index 0000000000000..6fe812bc58518
--- /dev/null
+++ b/src/core/server/elasticsearch/supported_server_response_check.ts
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+export const PRODUCT_RESPONSE_HEADER = 'x-elastic-product';
+/**
+ * Response headers check to determine if the response is from Elasticsearch
+ * @param headers Response headers
+ * @returns boolean
+ */
+// This check belongs to the elasticsearch service as a dedicated helper method.
+export const isSupportedEsServer = (headers: Record | null) => {
+ return !!headers && headers[PRODUCT_RESPONSE_HEADER] === 'Elasticsearch';
+};
diff --git a/src/core/server/saved_objects/service/lib/errors.test.ts b/src/core/server/saved_objects/service/lib/errors.test.ts
index a366dce626ec2..3bea693429254 100644
--- a/src/core/server/saved_objects/service/lib/errors.test.ts
+++ b/src/core/server/saved_objects/service/lib/errors.test.ts
@@ -439,4 +439,45 @@ describe('savedObjectsClient/errorTypes', () => {
});
});
});
+
+ describe('NotFoundEsUnavailableError', () => {
+ it('makes an error identifiable as an EsUnavailable error', () => {
+ const error = SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError('foo', 'bar');
+ expect(SavedObjectsErrorHelpers.isEsUnavailableError(error)).toBe(true);
+ });
+
+ it('returns a boom error', () => {
+ const error = SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError('foo', 'bar');
+ expect(error).toHaveProperty('isBoom', true);
+ });
+
+ it('decorates the error message with the saved object that was not found', () => {
+ const error = SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError('foo', 'bar');
+ expect(error.output.payload).toHaveProperty(
+ 'message',
+ 'x-elastic-product not present or not recognized: Saved object [foo/bar] not found'
+ );
+ });
+
+ describe('error.output', () => {
+ it('specifies the saved object that was not found', () => {
+ const error = SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(
+ 'foo',
+ 'bar'
+ );
+ expect(error.output.payload).toHaveProperty(
+ 'message',
+ 'x-elastic-product not present or not recognized: Saved object [foo/bar] not found'
+ );
+ });
+
+ it('sets statusCode to 503', () => {
+ const error = SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(
+ 'foo',
+ 'bar'
+ );
+ expect(error.output).toHaveProperty('statusCode', 503);
+ });
+ });
+ });
});
diff --git a/src/core/server/saved_objects/service/lib/errors.ts b/src/core/server/saved_objects/service/lib/errors.ts
index 581145c7c09d1..c1e1e9589b9ae 100644
--- a/src/core/server/saved_objects/service/lib/errors.ts
+++ b/src/core/server/saved_objects/service/lib/errors.ts
@@ -202,4 +202,12 @@ export class SavedObjectsErrorHelpers {
public static isGeneralError(error: Error | DecoratedError) {
return isSavedObjectsClientError(error) && error[code] === CODE_GENERAL_ERROR;
}
+
+ public static createGenericNotFoundEsUnavailableError(type: string, id: string) {
+ const notFoundError = this.createGenericNotFoundError(type, id);
+ return this.decorateEsUnavailableError(
+ new Error(`${notFoundError.message}`),
+ `x-elastic-product not present or not recognized`
+ );
+ }
}
diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js
index 78af9f0753374..c025adce29808 100644
--- a/src/core/server/saved_objects/service/lib/repository.test.js
+++ b/src/core/server/saved_objects/service/lib/repository.test.js
@@ -44,6 +44,8 @@ const createGenericNotFoundError = (...args) =>
SavedObjectsErrorHelpers.createGenericNotFoundError(...args).output.payload;
const createUnsupportedTypeError = (...args) =>
SavedObjectsErrorHelpers.createUnsupportedTypeError(...args).output.payload;
+const createGenericNotFoundEsUnavailableError = (...args) =>
+ SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(...args).output.payload;
describe('SavedObjectsRepository', () => {
let client;
@@ -2202,6 +2204,11 @@ describe('SavedObjectsRepository', () => {
createGenericNotFoundError(type, id)
);
};
+ const expectNotFoundEsUnavailableError = async (type, id) => {
+ await expect(savedObjectsRepository.delete(type, id)).rejects.toThrowError(
+ createGenericNotFoundEsUnavailableError(type, id)
+ );
+ };
it(`throws when options.namespace is '*'`, async () => {
await expect(
@@ -2221,7 +2228,11 @@ describe('SavedObjectsRepository', () => {
it(`throws when ES is unable to find the document during get`, async () => {
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ { found: false },
+ undefined,
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
expect(client.get).toHaveBeenCalledTimes(1);
@@ -2229,12 +2240,30 @@ describe('SavedObjectsRepository', () => {
it(`throws when ES is unable to find the index during get`, async () => {
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ {},
+ { statusCode: 404 },
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
expect(client.get).toHaveBeenCalledTimes(1);
});
+ it(`throws when ES is unable to find the document during get with missing Elasticsearch header`, async () => {
+ client.get.mockResolvedValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
+ );
+ await expectNotFoundEsUnavailableError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
+ });
+
+ it(`throws when ES is unable to find the index during get with missing Elasticsearch header`, async () => {
+ client.get.mockResolvedValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
+ );
+ await expectNotFoundEsUnavailableError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
+ });
+
it(`throws when the type is multi-namespace and the document exists, but not in this namespace`, async () => {
const response = getMockGetResponse({ type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, namespace);
client.get.mockResolvedValueOnce(
@@ -2278,7 +2307,7 @@ describe('SavedObjectsRepository', () => {
client.delete.mockResolvedValueOnce(
elasticsearchClientMock.createSuccessTransportRequestPromise({ result: 'not_found' })
);
- await expectNotFoundError(type, id);
+ await expectNotFoundEsUnavailableError(type, id);
expect(client.delete).toHaveBeenCalledTimes(1);
});
@@ -2288,7 +2317,7 @@ describe('SavedObjectsRepository', () => {
error: { type: 'index_not_found_exception' },
})
);
- await expectNotFoundError(type, id);
+ await expectNotFoundEsUnavailableError(type, id);
expect(client.delete).toHaveBeenCalledTimes(1);
});
@@ -3170,7 +3199,11 @@ describe('SavedObjectsRepository', () => {
createGenericNotFoundError(type, id)
);
};
-
+ const expectNotFoundEsUnavailableError = async (type, id) => {
+ await expect(savedObjectsRepository.get(type, id)).rejects.toThrowError(
+ createGenericNotFoundEsUnavailableError(type, id)
+ );
+ };
it(`throws when options.namespace is '*'`, async () => {
await expect(
savedObjectsRepository.get(type, id, { namespace: ALL_NAMESPACES_STRING })
@@ -3189,7 +3222,11 @@ describe('SavedObjectsRepository', () => {
it(`throws when ES is unable to find the document during get`, async () => {
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ { found: false },
+ undefined,
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
await expectNotFoundError(type, id);
expect(client.get).toHaveBeenCalledTimes(1);
@@ -3197,7 +3234,11 @@ describe('SavedObjectsRepository', () => {
it(`throws when ES is unable to find the index during get`, async () => {
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ {},
+ { statusCode: 404 },
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
await expectNotFoundError(type, id);
expect(client.get).toHaveBeenCalledTimes(1);
@@ -3213,6 +3254,15 @@ describe('SavedObjectsRepository', () => {
});
expect(client.get).toHaveBeenCalledTimes(1);
});
+
+ it(`throws when ES does not return the correct header when finding the document during get`, async () => {
+ client.get.mockResolvedValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
+ );
+ await expectNotFoundEsUnavailableError(type, id);
+
+ expect(client.get).toHaveBeenCalledTimes(1);
+ });
});
describe('returns', () => {
@@ -3314,9 +3364,12 @@ describe('SavedObjectsRepository', () => {
it('because alias is not used and actual object is not found', async () => {
const options = { namespace: undefined };
- const response = { found: false };
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ { found: false },
+ undefined,
+ { 'x-elastic-product': 'Elasticsearch' }
+ ) // for actual target
);
await expectNotFoundError(type, id, options);
@@ -3854,26 +3907,34 @@ describe('SavedObjectsRepository', () => {
if (registry.isMultiNamespace(type)) {
const mockGetResponse = getMockGetResponse({ type, id }, options?.namespace);
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise(mockGetResponse)
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ { ...mockGetResponse },
+ { statusCode: 200 },
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
}
client.update.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({
- _id: `${type}:${id}`,
- ...mockVersionProps,
- result: 'updated',
- // don't need the rest of the source for test purposes, just the namespace and namespaces attributes
- get: {
- _source: {
- namespaces: [options?.namespace ?? 'default'],
- namespace: options?.namespace,
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ {
+ _id: `${type}:${id}`,
+ ...mockVersionProps,
+ result: 'updated',
+ // don't need the rest of the source for test purposes, just the namespace and namespaces attributes
+ get: {
+ _source: {
+ namespaces: [options?.namespace ?? 'default'],
+ namespace: options?.namespace,
- // "includeOriginId" is not an option for the operation; however, if the existing saved object contains an originId attribute, the
- // operation will return it in the result. This flag is just used for test purposes to modify the mock cluster call response.
- ...(includeOriginId && { originId }),
+ // "includeOriginId" is not an option for the operation; however, if the existing saved object contains an originId attribute, the
+ // operation will return it in the result. This flag is just used for test purposes to modify the mock cluster call response.
+ ...(includeOriginId && { originId }),
+ },
},
},
- })
+ { statusCode: 200 },
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
const result = await savedObjectsRepository.update(type, id, attributes, options);
expect(client.get).toHaveBeenCalledTimes(registry.isMultiNamespace(type) ? 1 : 0);
@@ -4059,6 +4120,11 @@ describe('SavedObjectsRepository', () => {
createGenericNotFoundError(type, id)
);
};
+ const expectNotFoundEsUnavailableError = async (type, id) => {
+ await expect(savedObjectsRepository.update(type, id)).rejects.toThrowError(
+ createGenericNotFoundEsUnavailableError(type, id)
+ );
+ };
it(`throws when options.namespace is '*'`, async () => {
await expect(
@@ -4078,7 +4144,11 @@ describe('SavedObjectsRepository', () => {
it(`throws when ES is unable to find the document during get`, async () => {
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ { found: false },
+ undefined,
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
expect(client.get).toHaveBeenCalledTimes(1);
@@ -4086,12 +4156,32 @@ describe('SavedObjectsRepository', () => {
it(`throws when ES is unable to find the index during get`, async () => {
client.get.mockResolvedValueOnce(
- elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
+ elasticsearchClientMock.createSuccessTransportRequestPromise(
+ {},
+ { statusCode: 404 },
+ { 'x-elastic-product': 'Elasticsearch' }
+ )
);
await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
expect(client.get).toHaveBeenCalledTimes(1);
});
+ it(`throws when ES is unable to find the document during get with missing Elasticsearch header`, async () => {
+ client.get.mockResolvedValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({ found: false })
+ );
+ await expectNotFoundEsUnavailableError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
+ expect(client.get).toHaveBeenCalledTimes(1);
+ });
+
+ it(`throws when ES is unable to find the index during get with missing Elasticsearch`, async () => {
+ client.get.mockResolvedValueOnce(
+ elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
+ );
+ await expectNotFoundEsUnavailableError(MULTI_NAMESPACE_ISOLATED_TYPE, id);
+ expect(client.get).toHaveBeenCalledTimes(1);
+ });
+
it(`throws when type is multi-namespace and the document exists, but not in this namespace`, async () => {
const response = getMockGetResponse({ type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, namespace);
client.get.mockResolvedValueOnce(
diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts
index 6899f8613b07f..7ac4fe87bfc19 100644
--- a/src/core/server/saved_objects/service/lib/repository.ts
+++ b/src/core/server/saved_objects/service/lib/repository.ts
@@ -14,6 +14,7 @@ import {
REPOSITORY_RESOLVE_OUTCOME_STATS,
} from '../../../core_usage_data';
import type { ElasticsearchClient } from '../../../elasticsearch/';
+import { isSupportedEsServer } from '../../../elasticsearch';
import type { Logger } from '../../../logging';
import { getRootPropertiesObjects, IndexMapping } from '../../mappings';
import {
@@ -648,7 +649,7 @@ export class SavedObjectsRepository {
}
}
- const { body, statusCode } = await this.client.delete(
+ const { body, statusCode, headers } = await this.client.delete(
{
id: rawId,
index: this.getIndexForType(type),
@@ -665,9 +666,15 @@ export class SavedObjectsRepository {
const deleteDocNotFound = body.result === 'not_found';
const deleteIndexNotFound = body.error && body.error.type === 'index_not_found_exception';
+ const esServerSupported = isSupportedEsServer(headers);
if (deleteDocNotFound || deleteIndexNotFound) {
- // see "404s from missing index" above
- throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
+ if (esServerSupported) {
+ // see "404s from missing index" above
+ throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
+ } else {
+ // throw if we can't verify the response is from Elasticsearch
+ throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(type, id);
+ }
}
throw new Error(
@@ -1009,19 +1016,19 @@ export class SavedObjectsRepository {
if (!this._allowedTypes.includes(type)) {
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
-
const namespace = normalizeNamespace(options.namespace);
-
- const { body, statusCode } = await this.client.get(
+ const { body, statusCode, headers } = await this.client.get(
{
id: this._serializer.generateRawId(namespace, type, id),
index: this.getIndexForType(type),
},
{ ignore: [404] }
);
-
const indexNotFound = statusCode === 404;
-
+ // check if we have the elasticsearch header when index is not found and if we do, ensure it is Elasticsearch
+ if (!isFoundGetResponse(body) && !isSupportedEsServer(headers)) {
+ throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(type, id);
+ }
if (
!isFoundGetResponse(body) ||
indexNotFound ||
@@ -1030,7 +1037,6 @@ export class SavedObjectsRepository {
// see "404s from missing index" above
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
-
return getSavedObjectFromSource(this._registry, type, id, body);
}
@@ -1248,7 +1254,19 @@ export class SavedObjectsRepository {
_source_includes: ['namespace', 'namespaces', 'originId'],
require_alias: true,
})
+ .then((res) => {
+ const indexNotFound = res.statusCode === 404;
+ const esServerSupported = isSupportedEsServer(res.headers);
+ // check if we have the elasticsearch header when index is not found and if we do, ensure it is Elasticsearch
+ if (indexNotFound && !esServerSupported) {
+ throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(type, id);
+ }
+ return res;
+ })
.catch((err) => {
+ if (SavedObjectsErrorHelpers.isEsUnavailableError(err)) {
+ throw err;
+ }
if (SavedObjectsErrorHelpers.isNotFoundError(err)) {
// see "404s from missing index" above
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
@@ -2070,7 +2088,7 @@ export class SavedObjectsRepository {
* @param id The ID of the saved object.
* @param namespace The target namespace.
* @returns Raw document from Elasticsearch.
- * @throws Will throw an error if the saved object is not found, or if it doesn't include the target namespace.
+ * @throws Will throw an error if the saved object is not found, if it doesn't include the target namespace or if the response is not identifiable as an Elasticsearch response.
*/
private async preflightCheckIncludesNamespace(type: string, id: string, namespace?: string) {
if (!this._registry.isMultiNamespace(type)) {
@@ -2078,7 +2096,7 @@ export class SavedObjectsRepository {
}
const rawId = this._serializer.generateRawId(undefined, type, id);
- const { body, statusCode } = await this.client.get(
+ const { body, statusCode, headers } = await this.client.get(
{
id: rawId,
index: this.getIndexForType(type),
@@ -2087,6 +2105,14 @@ export class SavedObjectsRepository {
);
const indexFound = statusCode !== 404;
+
+ // check if we have the elasticsearch header when index is not found and if we do, ensure it is Elasticsearch
+ const esServerSupported = isSupportedEsServer(headers);
+
+ if (!isFoundGetResponse(body) && !esServerSupported) {
+ throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(type, id);
+ }
+
if (
!indexFound ||
!isFoundGetResponse(body) ||
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 7f2ce38a5bdd4..48b0c1488e81e 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -2523,6 +2523,8 @@ export class SavedObjectsErrorHelpers {
// (undocumented)
static createGenericNotFoundError(type?: string | null, id?: string | null): DecoratedError;
// (undocumented)
+ static createGenericNotFoundEsUnavailableError(type: string, id: string): DecoratedError;
+ // (undocumented)
static createIndexAliasNotFoundError(alias: string): DecoratedError;
// (undocumented)
static createInvalidVersionError(versionInput?: string): DecoratedError;