Skip to content

Commit 1ea9f11

Browse files
author
Christoph Büscher
authored
Change ScriptException status to 400 (bad request) (#30861)
Currently failures to compile a script usually lead to a ScriptException, which inherits the 500 INTERNAL_SERVER_ERROR from ElasticsearchException if it does not contain another root cause. Instead, this should be a 400 Bad Request error. This PR changes this more generally for script compilation errors by changing ScriptException to return 400 (bad request) as status code. Closes #12315
1 parent 7c5abc0 commit 1ea9f11

File tree

15 files changed

+71
-39
lines changed

15 files changed

+71
-39
lines changed

docs/painless/painless-debugging.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Which shows that the class of `doc.first` is
4848
"java_class": "org.elasticsearch.index.fielddata.ScriptDocValues$Longs",
4949
...
5050
},
51-
"status": 500
51+
"status": 400
5252
}
5353
---------------------------------------------------------
5454
// TESTRESPONSE[s/\.\.\./"script_stack": $body.error.script_stack, "script": $body.error.script, "lang": $body.error.lang, "caused_by": $body.error.caused_by, "root_cause": $body.error.root_cause, "reason": $body.error.reason/]

docs/reference/migration/migrate_7_0/scripting.asciidoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ the getter methods for date objects were deprecated. These methods have
1111
now been removed. Instead, use `.value` on `date` fields, or explicitly
1212
parse `long` fields into a date object using
1313
`Instance.ofEpochMillis(doc["myfield"].value)`.
14+
15+
==== Script errors will return as `400` error codes
16+
17+
Malformed scripts, either in search templates, ingest pipelines or search
18+
requests, return `400 - Bad request` while they would previously return
19+
`500 - Internal Server Error`. This also applies for stored scripts.

docs/reference/migration/migrate_7_0/search.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ The Search API returns `400 - Bad request` while it would previously return
4343
* the number of slices is too large
4444
* keep alive for scroll is too large
4545
* number of filters in the adjacency matrix aggregation is too large
46-
46+
* script compilation errors
4747

4848
==== Scroll queries cannot use the `request_cache` anymore
4949

modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
package org.elasticsearch.script.mustache;
2020

2121
import com.github.mustachejava.Mustache;
22+
import com.github.mustachejava.MustacheException;
2223
import com.github.mustachejava.MustacheFactory;
2324

24-
import java.io.StringReader;
2525
import org.apache.logging.log4j.Logger;
2626
import org.apache.logging.log4j.message.ParameterizedMessage;
2727
import org.apache.logging.log4j.util.Supplier;
@@ -31,12 +31,15 @@
3131
import org.elasticsearch.script.Script;
3232
import org.elasticsearch.script.ScriptContext;
3333
import org.elasticsearch.script.ScriptEngine;
34+
import org.elasticsearch.script.ScriptException;
3435
import org.elasticsearch.script.TemplateScript;
3536

3637
import java.io.Reader;
38+
import java.io.StringReader;
3739
import java.io.StringWriter;
3840
import java.security.AccessController;
3941
import java.security.PrivilegedAction;
42+
import java.util.Collections;
4043
import java.util.Map;
4144

4245
/**
@@ -66,9 +69,14 @@ public <T> T compile(String templateName, String templateSource, ScriptContext<T
6669
}
6770
final MustacheFactory factory = createMustacheFactory(options);
6871
Reader reader = new StringReader(templateSource);
69-
Mustache template = factory.compile(reader, "query-template");
70-
TemplateScript.Factory compiled = params -> new MustacheExecutableScript(template, params);
71-
return context.factoryClazz.cast(compiled);
72+
try {
73+
Mustache template = factory.compile(reader, "query-template");
74+
TemplateScript.Factory compiled = params -> new MustacheExecutableScript(template, params);
75+
return context.factoryClazz.cast(compiled);
76+
} catch (MustacheException ex) {
77+
throw new ScriptException(ex.getMessage(), ex, Collections.emptyList(), templateSource, NAME);
78+
}
79+
7280
}
7381

7482
private CustomMustacheFactory createMustacheFactory(Map<String, String> options) {

modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/MustacheTests.java

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
*/
1919
package org.elasticsearch.script.mustache;
2020

21+
import org.elasticsearch.common.bytes.BytesReference;
22+
import org.elasticsearch.common.xcontent.XContentBuilder;
23+
import org.elasticsearch.common.xcontent.XContentHelper;
24+
import org.elasticsearch.script.ScriptEngine;
25+
import org.elasticsearch.script.ScriptException;
26+
import org.elasticsearch.script.TemplateScript;
27+
import org.elasticsearch.test.ESTestCase;
28+
import org.hamcrest.Matcher;
29+
2130
import java.net.URLEncoder;
2231
import java.nio.charset.StandardCharsets;
2332
import java.util.Arrays;
@@ -29,15 +38,6 @@
2938
import java.util.Map;
3039
import java.util.Set;
3140

32-
import com.github.mustachejava.MustacheException;
33-
import org.elasticsearch.common.bytes.BytesReference;
34-
import org.elasticsearch.common.xcontent.XContentBuilder;
35-
import org.elasticsearch.common.xcontent.XContentHelper;
36-
import org.elasticsearch.script.ScriptEngine;
37-
import org.elasticsearch.script.TemplateScript;
38-
import org.elasticsearch.test.ESTestCase;
39-
import org.hamcrest.Matcher;
40-
4141
import static java.util.Collections.singleton;
4242
import static java.util.Collections.singletonMap;
4343
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
@@ -225,11 +225,17 @@ public void testSimpleListToJSON() throws Exception {
225225
}
226226

227227
public void testsUnsupportedTagsToJson() {
228-
MustacheException e = expectThrows(MustacheException.class, () -> compile("{{#toJson}}{{foo}}{{bar}}{{/toJson}}"));
228+
final String script = "{{#toJson}}{{foo}}{{bar}}{{/toJson}}";
229+
ScriptException e = expectThrows(ScriptException.class, () -> compile(script));
229230
assertThat(e.getMessage(), containsString("Mustache function [toJson] must contain one and only one identifier"));
231+
assertEquals(MustacheScriptEngine.NAME, e.getLang());
232+
assertEquals(script, e.getScript());
230233

231-
e = expectThrows(MustacheException.class, () -> compile("{{#toJson}}{{/toJson}}"));
234+
final String script2 = "{{#toJson}}{{/toJson}}";
235+
e = expectThrows(ScriptException.class, () -> compile(script2));
232236
assertThat(e.getMessage(), containsString("Mustache function [toJson] must contain one and only one identifier"));
237+
assertEquals(MustacheScriptEngine.NAME, e.getLang());
238+
assertEquals(script2, e.getScript());
233239
}
234240

235241
public void testEmbeddedToJSON() throws Exception {
@@ -312,11 +318,17 @@ public void testJoinWithToJson() {
312318
}
313319

314320
public void testsUnsupportedTagsJoin() {
315-
MustacheException e = expectThrows(MustacheException.class, () -> compile("{{#join}}{{/join}}"));
321+
final String script = "{{#join}}{{/join}}";
322+
ScriptException e = expectThrows(ScriptException.class, () -> compile(script));
316323
assertThat(e.getMessage(), containsString("Mustache function [join] must contain one and only one identifier"));
324+
assertEquals(MustacheScriptEngine.NAME, e.getLang());
325+
assertEquals(script, e.getScript());
317326

318-
e = expectThrows(MustacheException.class, () -> compile("{{#join delimiter='a'}}{{/join delimiter='b'}}"));
327+
final String script2 = "{{#join delimiter='a'}}{{/join delimiter='b'}}";
328+
e = expectThrows(ScriptException.class, () -> compile(script2));
319329
assertThat(e.getMessage(), containsString("Mismatched start/end tags"));
330+
assertEquals(MustacheScriptEngine.NAME, e.getLang());
331+
assertEquals(script2, e.getScript());
320332
}
321333

322334
public void testJoinWithCustomDelimiter() {

modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
id: "non_existing"
3636

3737
- do:
38-
catch: request
38+
catch: bad_request
3939
put_script:
4040
id: "1"
4141
context: "search"

modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ setup:
133133
---
134134
"Scripted Field with script error":
135135
- do:
136-
catch: request
136+
catch: bad_request
137137
search:
138138
body:
139139
script_fields:

modules/reindex/src/test/resources/rest-api-spec/test/reindex/35_search_failures.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
indices.refresh: {}
1818

1919
- do:
20-
catch: request
20+
catch: bad_request
2121
reindex:
2222
body:
2323
source:

modules/reindex/src/test/resources/rest-api-spec/test/reindex/85_scripting.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@
446446
indices.refresh: {}
447447

448448
- do:
449-
catch: request
449+
catch: bad_request
450450
reindex:
451451
refresh: true
452452
body:

modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/35_search_failure.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
indices.refresh: {}
1818

1919
- do:
20-
catch: request
20+
catch: bad_request
2121
update_by_query:
2222
index: source
2323
body:

modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/80_scripting.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@
434434
indices.refresh: {}
435435

436436
- do:
437-
catch: request
437+
catch: bad_request
438438
update_by_query:
439439
index: twitter
440440
refresh: true

qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@
332332
wait_for_status: green
333333

334334
- do:
335-
catch: request
335+
catch: bad_request
336336
ingest.put_pipeline:
337337
id: "my_pipeline_1"
338338
body: >
@@ -348,5 +348,5 @@
348348
]
349349
}
350350
- match: { error.header.processor_type: "set" }
351-
- match: { error.type: "general_script_exception" }
352-
- match: { error.reason: "Failed to compile inline script [{{#join}}{{/join}}] using lang [mustache]" }
351+
- match: { error.type: "script_exception" }
352+
- match: { error.reason: "Mustache function [join] must contain one and only one identifier" }

qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
---
9090
"Test script processor with syntax error in inline script":
9191
- do:
92-
catch: request
92+
catch: bad_request
9393
ingest.put_pipeline:
9494
id: "my_pipeline"
9595
body: >

server/src/main/java/org/elasticsearch/script/ScriptException.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
package org.elasticsearch.script;
22

3+
import org.elasticsearch.ElasticsearchException;
4+
import org.elasticsearch.common.Strings;
5+
import org.elasticsearch.common.io.stream.StreamInput;
6+
import org.elasticsearch.common.io.stream.StreamOutput;
7+
import org.elasticsearch.common.xcontent.ToXContent;
8+
import org.elasticsearch.common.xcontent.XContentBuilder;
9+
import org.elasticsearch.common.xcontent.XContentFactory;
10+
import org.elasticsearch.rest.RestStatus;
11+
312
/*
413
* Licensed to Elasticsearch under one or more contributor
514
* license agreements. See the NOTICE file distributed with
@@ -25,14 +34,6 @@
2534
import java.util.List;
2635
import java.util.Objects;
2736

28-
import org.elasticsearch.ElasticsearchException;
29-
import org.elasticsearch.common.Strings;
30-
import org.elasticsearch.common.io.stream.StreamInput;
31-
import org.elasticsearch.common.io.stream.StreamOutput;
32-
import org.elasticsearch.common.xcontent.ToXContent;
33-
import org.elasticsearch.common.xcontent.XContentBuilder;
34-
import org.elasticsearch.common.xcontent.XContentFactory;
35-
3637
/**
3738
* Exception from a scripting engine.
3839
* <p>
@@ -132,4 +133,9 @@ public String toJsonString() {
132133
throw new RuntimeException(e);
133134
}
134135
}
136+
137+
@Override
138+
public RestStatus status() {
139+
return RestStatus.BAD_REQUEST;
140+
}
135141
}

x-pack/qa/smoke-test-watcher/src/test/resources/rest-api-spec/test/painless/40_exception.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
wait_for_status: green
66

77
- do:
8-
catch: request
8+
catch: bad_request
99
xpack.watcher.put_watch:
1010
id: "my_exe_watch"
1111
body: >
@@ -33,7 +33,7 @@
3333
}
3434
3535
- is_true: error.script_stack
36-
- match: { status: 500 }
36+
- match: { status: 400 }
3737

3838
---
3939
"Test painless exceptions are returned when logging a broken response":

0 commit comments

Comments
 (0)