Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
39 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
c77abde
Merge branch 'main' into exception-json
thecoop Nov 6, 2024
93da694
Add a basic deprecation warning that the JSON format is changing in v9
thecoop Oct 14, 2024
872699c
Merge branch 'main' into exception-json
thecoop Nov 13, 2024
cc2bc6a
Revert "Add a deprecation warning that the JSON format of non-detaile…
thecoop Nov 13, 2024
980a449
Propagate changes from deprecation PRs
thecoop Nov 13, 2024
76f9185
Update tests
thecoop Nov 13, 2024
4726363
Update docs/changelog/90529.yaml
thecoop Nov 13, 2024
cb964f0
Revert "Update docs/changelog/90529.yaml"
thecoop Nov 13, 2024
aef3b2d
Use more randomness in detailed parameter
thecoop Nov 13, 2024
cd81c30
splotless
thecoop Nov 13, 2024
108dd01
Update test
thecoop Nov 14, 2024
c4a929c
Merge branch 'main' into exception-json
thecoop Nov 14, 2024
9ccb15c
Don't want stray warnings here
thecoop Nov 14, 2024
66c8b91
Merge branch 'main' into exception-json
elasticmachine Nov 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
26 changes: 26 additions & 0 deletions docs/changelog/90529.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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.
To use the previous structure for non-detailed errors, use the v8 REST API.
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.
To use the previous structure for non-detailed errors, use the v8 REST API.
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
7 changes: 4 additions & 3 deletions server/src/main/java/org/elasticsearch/rest/RestResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.Releasable;
import org.elasticsearch.core.Releasables;
import org.elasticsearch.core.RestApiVersion;
import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.xcontent.XContentBuilder;

Expand Down Expand Up @@ -146,12 +147,12 @@ public RestResponse(RestChannel channel, RestStatus status, Exception e) throws
params = new ToXContent.DelegatingMapParams(singletonMap(REST_EXCEPTION_SKIP_STACK_TRACE, "false"), params);
}

if (channel.detailedErrorsEnabled() == false) {
if (channel.request().getRestApiVersion() == RestApiVersion.V_8 && channel.detailedErrorsEnabled() == false) {
deprecationLogger.warn(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this change to critical now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, there's (probably) nothing directly for users to do; we consider the existing behaviour a bug, as it's surprising the format changes with non-detailed errors.

DeprecationCategory.API,
"http_detailed_errors",
"The JSON format of non-detailed errors will change in Elasticsearch 9.0 to match the JSON structure"
+ " used for detailed errors. To keep using the existing format, use the V8 REST API."
"The JSON format of non-detailed errors has changed in Elasticsearch 9.0 to match the JSON structure"
+ " used for detailed errors."
);
}

Expand Down
Loading