Skip to content

Commit 99b98fa

Browse files
authored
Core: Pick inner most parse exception as root cause (#30270)
Just like `ElasticsearchException`, the inner most `XContentParseException` tends to contain the root cause of the exception and show be show to the user in the `root_cause` field. The effectively undoes most of the changes that #29373 made to the `root_cause` for parsing exceptions. The `type` field still changes from `parse_exception` to `x_content_parse_exception`, but this seems like a fairly safe change. `ElasticsearchWrapperException` *looks* tempting to implement this but the behavior isn't quite right. `ElasticsearchWrapperExceptions` are entirely unwrapped until the cause no longer `implements ElasticsearchWrapperException` but `XContentParseException` should be unwrapped until its cause is no longer an `XContentParseException` but no further. In other words, `ElasticsearchWrapperException` are unwrapped one step too far. Closes #30261
1 parent b9e1860 commit 99b98fa

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

server/src/main/java/org/elasticsearch/ElasticsearchException.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.common.logging.LoggerMessageFormat;
3232
import org.elasticsearch.common.xcontent.ToXContentFragment;
3333
import org.elasticsearch.common.xcontent.XContentBuilder;
34+
import org.elasticsearch.common.xcontent.XContentParseException;
3435
import org.elasticsearch.common.xcontent.XContentParser;
3536
import org.elasticsearch.index.Index;
3637
import org.elasticsearch.index.shard.ShardId;
@@ -635,8 +636,25 @@ public ElasticsearchException[] guessRootCauses() {
635636
public static ElasticsearchException[] guessRootCauses(Throwable t) {
636637
Throwable ex = ExceptionsHelper.unwrapCause(t);
637638
if (ex instanceof ElasticsearchException) {
639+
// ElasticsearchException knows how to guess its own root cause
638640
return ((ElasticsearchException) ex).guessRootCauses();
639641
}
642+
if (ex instanceof XContentParseException) {
643+
/*
644+
* We'd like to unwrap parsing exceptions to the inner-most
645+
* parsing exception because that is generally the most interesting
646+
* exception to return to the user. If that exception is caused by
647+
* an ElasticsearchException we'd like to keep unwrapping because
648+
* ElasticserachExceptions tend to contain useful information for
649+
* the user.
650+
*/
651+
Throwable cause = ex.getCause();
652+
if (cause != null) {
653+
if (cause instanceof XContentParseException || cause instanceof ElasticsearchException) {
654+
return guessRootCauses(ex.getCause());
655+
}
656+
}
657+
}
640658
return new ElasticsearchException[]{new ElasticsearchException(t.getMessage(), t) {
641659
@Override
642660
protected String getExceptionName() {

server/src/main/java/org/elasticsearch/ElasticsearchWrapperException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919

2020
package org.elasticsearch;
2121

22+
/**
23+
* An exception that is meant to be "unwrapped" when sent back to the user
24+
* as an error because its is {@link #getCause() cause}, if non-null is
25+
* <strong>always</strong> more useful to the user than the exception itself.
26+
*/
2227
public interface ElasticsearchWrapperException {
23-
2428
Throwable getCause();
2529
}

server/src/test/java/org/elasticsearch/ElasticsearchExceptionTests.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.elasticsearch.common.xcontent.XContentFactory;
4242
import org.elasticsearch.common.xcontent.XContentHelper;
4343
import org.elasticsearch.common.xcontent.XContentLocation;
44+
import org.elasticsearch.common.xcontent.XContentParseException;
4445
import org.elasticsearch.common.xcontent.XContentParser;
4546
import org.elasticsearch.common.xcontent.XContentType;
4647
import org.elasticsearch.discovery.DiscoverySettings;
@@ -78,6 +79,7 @@
7879
import static org.hamcrest.CoreMatchers.hasItems;
7980
import static org.hamcrest.Matchers.equalTo;
8081
import static org.hamcrest.Matchers.hasSize;
82+
import static org.hamcrest.Matchers.instanceOf;
8183
import static org.hamcrest.Matchers.startsWith;
8284

8385
public class ElasticsearchExceptionTests extends ESTestCase {
@@ -124,13 +126,13 @@ public void testGuessRootCause() {
124126
} else {
125127
rootCauses = ElasticsearchException.guessRootCauses(randomBoolean() ? new RemoteTransportException("remoteboom", ex) : ex);
126128
}
127-
assertEquals(ElasticsearchException.getExceptionName(rootCauses[0]), "parsing_exception");
128-
assertEquals(rootCauses[0].getMessage(), "foobar");
129+
assertEquals("parsing_exception", ElasticsearchException.getExceptionName(rootCauses[0]));
130+
assertEquals("foobar", rootCauses[0].getMessage());
129131

130132
ElasticsearchException oneLevel = new ElasticsearchException("foo", new RuntimeException("foobar"));
131133
rootCauses = oneLevel.guessRootCauses();
132-
assertEquals(ElasticsearchException.getExceptionName(rootCauses[0]), "exception");
133-
assertEquals(rootCauses[0].getMessage(), "foo");
134+
assertEquals("exception", ElasticsearchException.getExceptionName(rootCauses[0]));
135+
assertEquals("foo", rootCauses[0].getMessage());
134136
}
135137
{
136138
ShardSearchFailure failure = new ShardSearchFailure(
@@ -146,20 +148,40 @@ public void testGuessRootCause() {
146148
assertEquals(rootCauses.length, 2);
147149
assertEquals(ElasticsearchException.getExceptionName(rootCauses[0]), "parsing_exception");
148150
assertEquals(rootCauses[0].getMessage(), "foobar");
149-
assertEquals(((ParsingException) rootCauses[0]).getLineNumber(), 1);
150-
assertEquals(((ParsingException) rootCauses[0]).getColumnNumber(), 2);
151-
assertEquals(ElasticsearchException.getExceptionName(rootCauses[1]), "query_shard_exception");
152-
assertEquals((rootCauses[1]).getIndex().getName(), "foo1");
153-
assertEquals(rootCauses[1].getMessage(), "foobar");
151+
assertEquals(1, ((ParsingException) rootCauses[0]).getLineNumber());
152+
assertEquals(2, ((ParsingException) rootCauses[0]).getColumnNumber());
153+
assertEquals("query_shard_exception", ElasticsearchException.getExceptionName(rootCauses[1]));
154+
assertEquals("foo1", rootCauses[1].getIndex().getName());
155+
assertEquals("foobar", rootCauses[1].getMessage());
154156
}
155157

156158
{
157159
final ElasticsearchException[] foobars = ElasticsearchException.guessRootCauses(new IllegalArgumentException("foobar"));
158160
assertEquals(foobars.length, 1);
159-
assertTrue(foobars[0] instanceof ElasticsearchException);
160-
assertEquals(foobars[0].getMessage(), "foobar");
161-
assertEquals(foobars[0].getCause().getClass(), IllegalArgumentException.class);
162-
assertEquals(foobars[0].getExceptionName(), "illegal_argument_exception");
161+
assertThat(foobars[0], instanceOf(ElasticsearchException.class));
162+
assertEquals("foobar", foobars[0].getMessage());
163+
assertEquals(IllegalArgumentException.class, foobars[0].getCause().getClass());
164+
assertEquals("illegal_argument_exception", foobars[0].getExceptionName());
165+
}
166+
167+
{
168+
XContentParseException inner = new XContentParseException(null, "inner");
169+
XContentParseException outer = new XContentParseException(null, "outer", inner);
170+
final ElasticsearchException[] causes = ElasticsearchException.guessRootCauses(outer);
171+
assertEquals(causes.length, 1);
172+
assertThat(causes[0], instanceOf(ElasticsearchException.class));
173+
assertEquals("inner", causes[0].getMessage());
174+
assertEquals("x_content_parse_exception", causes[0].getExceptionName());
175+
}
176+
177+
{
178+
ElasticsearchException inner = new ElasticsearchException("inner");
179+
XContentParseException outer = new XContentParseException(null, "outer", inner);
180+
final ElasticsearchException[] causes = ElasticsearchException.guessRootCauses(outer);
181+
assertEquals(causes.length, 1);
182+
assertThat(causes[0], instanceOf(ElasticsearchException.class));
183+
assertEquals("inner", causes[0].getMessage());
184+
assertEquals("exception", causes[0].getExceptionName());
163185
}
164186
}
165187

0 commit comments

Comments
 (0)