Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Output a consistent format when generating error json #90529

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b0c0682
Output a consistent format when generating error json
thecoop Sep 29, 2022
4cd9a4e
Update docs/changelog/90529.yaml
thecoop Sep 29, 2022
a731fa6
Update changelog with issue number
thecoop Sep 29, 2022
cec444a
Update RestResponseTests
thecoop Sep 29, 2022
f3d60c8
PR comments
thecoop Sep 30, 2022
6bd5e56
Merge remote-tracking branch 'upstream/main' into exception-json
thecoop Sep 30, 2022
744626c
spotless
thecoop Sep 30, 2022
45b9806
Update docs/changelog/90529.yaml
thecoop Sep 30, 2022
7532693
Update docs/changelog/90529.yaml
thecoop Sep 30, 2022
1e75fce
Update docs/changelog/90529.yaml
thecoop Sep 30, 2022
750eb7d
Update changelog with change details
thecoop Sep 30, 2022
93dc953
Update docs/changelog/90529.yaml
thecoop Oct 3, 2022
d293dcb
Match the method parameter types
thecoop Oct 3, 2022
309b076
Merge remote-tracking branch 'upstream/main' into exception-json
thecoop Nov 17, 2022
ab3041c
Update docs
thecoop Nov 17, 2022
f185846
Merge branch 'main' into exception-json
thecoop Nov 17, 2022
459acf4
Update docs/changelog/90529.yaml
thecoop Feb 8, 2023
32a9f9e
Merge branch 'main' into exception-json
thecoop Feb 10, 2023
02862e9
Revert "Update docs/changelog/90529.yaml"
thecoop Feb 10, 2023
14b55f9
Update docs/changelog/90529.yaml
thecoop Apr 26, 2023
dca6d31
Merge branch 'main' into exception-json
thecoop Oct 10, 2024
a2548ab
Revert "Update docs/changelog/90529.yaml"
thecoop Oct 10, 2024
8396ba3
Merge branch 'main' into exception-json
elasticmachine Oct 10, 2024
798be45
Merge remote-tracking branch 'upstream/main' into exception-json
thecoop Oct 18, 2024
6f253c4
Add V8 rest API formats for compatibility
thecoop Oct 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/changelog/90529.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pr: 90529
summary: Output a consistent format when generating error json
area: Infra/REST API
type: "breaking"
issues:
- 89387
breaking:
title: Error JSON structure has changed when detailed errors are disabled
area: REST API
details: |-
This change modifies the JSON format of error messages returned to REST clients
when detailed messages are turned off.
Previously, JSON returned when an exception occurred, and `http.detailed_errors.enabled: false` was set,
just consisted of a single `"error"` text field with some basic information.
Setting `http.detailed_errors.enabled: true` (the default) changed this field
to an object with more detailed information.
With this change, non-detailed errors now have the same structure as detailed errors. `"error"` will now always
be an object with, at a minimum, a `"type"` and `"reason"` field. Additional fields are included when detailed
errors are enabled.
impact: "If you have set `http.detailed_errors.enabled: false` (the default is `true`)\
\ the structure of JSON when any exceptions occur now matches the structure when\
\ detailed errors are enabled."
notable: false
8 changes: 3 additions & 5 deletions docs/reference/modules/http.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,9 @@ NOTE: This header is only returned when the setting is set to `true`.

`http.detailed_errors.enabled`::
(<<static-cluster-setting,Static>>, boolean)
Configures whether detailed error reporting in HTTP responses is enabled.
Defaults to `true`, which means that HTTP requests that include the
<<common-options-error-options,`?error_trace` parameter>> will return a
detailed error message including a stack trace if they encounter an exception.
If set to `false`, requests with the `?error_trace` parameter are rejected.
Configures whether detailed error reporting in HTTP responses is enabled. Defaults to `true`.
When this option is set to `false`, only basic information is returned if an error occurs in the request,
and requests with <<common-options-error-options,`?error_trace` parameter>> set are rejected.

`http.pipelining.max_events`::
(<<static-cluster-setting,Static>>, integer)
Expand Down
54 changes: 39 additions & 15 deletions server/src/main/java/org/elasticsearch/ElasticsearchException.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
import org.elasticsearch.core.CheckedFunction;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.RestApiVersion;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.core.UpdateForV10;
import org.elasticsearch.health.node.action.HealthNodeNotDiscoveredException;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.mapper.DocumentParsingException;
Expand Down Expand Up @@ -611,23 +613,31 @@ protected static void generateThrowableXContent(XContentBuilder builder, Params
*/
public static XContentBuilder generateFailureXContent(XContentBuilder builder, Params params, @Nullable Exception e, boolean detailed)
throws IOException {
// No exception to render as an error
if (builder.getRestApiVersion() == RestApiVersion.V_8) {
if (e == null) {
return builder.field(ERROR, "unknown");
}
if (detailed == false) {
return generateNonDetailedFailureXContentV8(builder, e);
}
// else fallthrough
}

if (e == null) {
return builder.field(ERROR, "unknown");
// No exception to render as an error
builder.startObject(ERROR);
builder.field(TYPE, "unknown");
builder.field(REASON, "unknown");
return builder.endObject();
}

// Render the exception with a simple message
if (detailed == false) {
String message = "No ElasticsearchException found";
Throwable t = e;
for (int counter = 0; counter < 10 && t != null; counter++) {
if (t instanceof ElasticsearchException) {
message = t.getClass().getSimpleName() + "[" + t.getMessage() + "]";
break;
}
t = t.getCause();
}
return builder.field(ERROR, message);
// just render the type & message
Throwable t = ExceptionsHelper.unwrapCause(e);
builder.startObject(ERROR);
builder.field(TYPE, getExceptionName(t));
builder.field(REASON, t.getMessage());
return builder.endObject();
}

// Render the exception with all details
Expand All @@ -646,6 +656,20 @@ public static XContentBuilder generateFailureXContent(XContentBuilder builder, P
return builder.endObject();
}

@UpdateForV10(owner = UpdateForV10.Owner.CORE_INFRA) // remove V8 API
private static XContentBuilder generateNonDetailedFailureXContentV8(XContentBuilder builder, @Nullable Exception e) throws IOException {
String message = "No ElasticsearchException found";
Throwable t = e;
for (int counter = 0; counter < 10 && t != null; counter++) {
if (t instanceof ElasticsearchException) {
message = t.getClass().getSimpleName() + "[" + t.getMessage() + "]";
break;
}
t = t.getCause();
}
return builder.field(ERROR, message);
}

/**
* Parses the output of {@link #generateFailureXContent(XContentBuilder, Params, Exception, boolean)}
*/
Expand Down Expand Up @@ -729,8 +753,8 @@ public static String getExceptionName(Throwable ex) {

static String buildMessage(String type, String reason, String stack) {
StringBuilder message = new StringBuilder("Elasticsearch exception [");
message.append(TYPE).append('=').append(type).append(", ");
message.append(REASON).append('=').append(reason);
message.append(TYPE).append('=').append(type);
message.append(", ").append(REASON).append('=').append(reason);
if (stack != null) {
message.append(", ").append(STACK_TRACE).append('=').append(stack);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.core.CheckedFunction;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.RestApiVersion;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.plugins.internal.XContentParserDecorator;
import org.elasticsearch.xcontent.DeprecationHandler;
Expand Down Expand Up @@ -626,7 +627,22 @@ public static BytesReference toXContent(ChunkedToXContent toXContent, XContentTy
*/
public static BytesReference toXContent(ToXContent toXContent, XContentType xContentType, Params params, boolean humanReadable)
throws IOException {
try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent())) {
return toXContent(toXContent, xContentType, RestApiVersion.current(), params, humanReadable);
}

/**
* Returns the bytes that represent the XContent output of the provided {@link ToXContent} object, using the provided
* {@link XContentType}. Wraps the output into a new anonymous object according to the value returned
* by the {@link ToXContent#isFragment()} method returns.
*/
public static BytesReference toXContent(
ToXContent toXContent,
XContentType xContentType,
RestApiVersion restApiVersion,
Params params,
boolean humanReadable
) throws IOException {
try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent(), restApiVersion)) {
builder.humanReadable(humanReadable);
if (toXContent.isFragment()) {
builder.startObject();
Expand Down
Loading