diff --git a/troubleshoot/elasticsearch/painless-array-list-manipulation-errors.md b/troubleshoot/elasticsearch/painless-array-list-manipulation-errors.md new file mode 100644 index 0000000000..a30ff995a5 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-array-list-manipulation-errors.md @@ -0,0 +1,164 @@ +--- +navigation_title: Array manipulation errors +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot array manipulation errors in Painless + +Follow these guidelines to avoid array (list) access errors in your Painless scripts. + +An array `index_out_of_bounds_exception` occurs when a script tries to access an element at a position that does not exist in the array. For example, if an array has two elements, trying to access a third element triggers this exception. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "index_out_of_bounds_exception", + "reason": "index_out_of_bounds_exception: Index 2 out of bounds for length 2" + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "blog_posts", + "node": "hupWdkj_RtmThGjNUiIt_w", + "reason": { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)", + "java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)", + "java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)", + "java.base/java.util.Objects.checkIndex(Objects.java:365)", + "java.base/java.util.ArrayList.get(ArrayList.java:428)", + """return keywords[2].toUpperCase(); + """, + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 76, + "start": 61, + "end": 105 + }, + "caused_by": { + "type": "index_out_of_bounds_exception", + "reason": "index_out_of_bounds_exception: Index 2 out of bounds for length 2" + } + } + } + ], + "caused_by": { + "type": "index_out_of_bounds_exception", + "reason": "index_out_of_bounds_exception: Index 2 out of bounds for length 2" + } + }, + "status": 400 +} +``` + +## Problematic code + +```json +{ + "aggs": { + "third_tag_stats": { + "terms": { + "script": { + "source": """ + def keywords = params._source.tags; + + return keywords[2].toUpperCase(); + """, + "lang": "painless" + } + } + } + } +} +``` + +## Root cause + +The error occurs because the script tries to access index 2 (the third element) in an array that only has two elements (indices 0, 1). Arrays in Painless are zero-indexed, so accessing an index greater than or equal to the array size causes an exception. + +## Solution: Check array bounds before accessing + +Always verify the size of an array before accessing specific indices: + +```json +GET blog_posts/_search +{ + "size": 0, + "aggs": { + "third_tag_stats": { + "terms": { + "script": { + "source": """ + def keywords = params._source.tags; + + if (keywords.size() > 2) { + return keywords[2].toUpperCase(); + } else { + return "NO_THIRD_TAG"; + } + """, + "lang": "painless" + } + } + } + } +} +``` + +## Sample document + +```json +POST blog_posts/_doc +{ + "title": "Getting Started with Elasticsearch", + "content": "Learn the basics...", + "tags": ["elasticsearch", "tutorial"] +} +``` + +## Results + +```json +{ + ..., + "hits": { + ... + }, + "aggregations": { + "third_tag_stats": { + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0, + "buckets": [ + { + "key": "NO_THIRD_TAG", + "doc_count": 1 + } + ] + } + } +} +``` + +## Notes + +* **Array bounds:** Always check the size of an array before accessing specific indices. +* **Zero-indexed:** Remember that arrays start at index 0, so `size() - 1` is the last valid index. +* **Empty arrays:** Handle cases where arrays might be completely empty (`size() == 0`). diff --git a/troubleshoot/elasticsearch/painless-date-math-errors.md b/troubleshoot/elasticsearch/painless-date-math-errors.md new file mode 100644 index 0000000000..6c500f5224 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-date-math-errors.md @@ -0,0 +1,109 @@ +--- +navigation_title: Date math errors +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot date math errors in Painless + +Follow these guidelines to avoid [date](elasticsearch://reference/scripting-languages/painless/using-datetime-in-painless.md) operation errors in your Painless scripts. + +When you work with date fields in runtime mappings, accessing methods directly on the document field object can cause errors if the proper value accessor is not used. + +## Error + +```json +{ + "error": { + "root_cause": [ + { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + """emit(orderDate.toInstant().toEpochMilli() + 14400000); + """, + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 75, + "start": 61, + "end": 124 + } + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "kibana_sample_data_ecommerce", + "node": "CxMTEjvKSEC0k0aTr4OM3A", + "reason": { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + """emit(orderDate.toInstant().toEpochMilli() + 14400000); + """, + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 75, + "start": 61, + "end": 124 + }, + "caused_by": { + "type": "illegal_argument_exception", + "reason": "dynamic method [org.elasticsearch.index.fielddata.ScriptDocValues.Dates, toInstant/0] not found" + } + } + } + ] + }, + "status": 400 +} +``` + +## Problematic code + +```json +"script": { + "lang": "painless", + "source": """ + def orderDate = doc['order_date']; + emit(orderDate.toInstant().toEpochMilli() + 14400000); + """ +} +``` + +## Root cause + +The script attempts to call `toInstant()` directly on a `ScriptDocValues.Dates` object. Date fields in Painless require accessing the `.value` property to get the actual date value before calling date methods. + +## Solution + +Access the date value using `.value` before calling date methods: + +```json +"script": { + "lang": "painless", + "source": """ + def orderDate = doc['order_date'].value; // Appended `.value` to the method. + emit(orderDate.toInstant().toEpochMilli() + 14400000); + """ +} +``` + +## Notes + +* Always use `.value` when accessing single values from document fields in Painless. +* Check for empty fields when the field might not exist in all documents. +* Date arithmetic should be performed on the actual date value, not the field container object. diff --git a/troubleshoot/elasticsearch/painless-field-not-found.md b/troubleshoot/elasticsearch/painless-field-not-found.md new file mode 100644 index 0000000000..7821e3b7c7 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-field-not-found.md @@ -0,0 +1,245 @@ +--- +navigation_title: Field not found errors +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot field not found errors in Painless + +Follow these guidelines to avoid field access errors in your Painless scripts. + +When you work with document fields, attempting to access fields that don't exist in all documents or aren't properly mapped leads to field not found exceptions, causing script failures. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "org.elasticsearch.server@9.0.0/org.elasticsearch.index.fielddata.ScriptDocValues.throwIfEmpty(ScriptDocValues.java:93)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.index.fielddata.ScriptDocValues$Longs.get(ScriptDocValues.java:117)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.index.fielddata.ScriptDocValues$Longs.getValue(ScriptDocValues.java:112)", + "doc['author_score'].value > 50 ? doc['author_score'].value : 1", + " ^---- HERE" + ], + "script": "doc['author_score'].value > 50 ? doc['author_score'].value : 1", + "lang": "painless", + "position": { + "offset": 19, + "start": 0, + "end": 62 + } + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "articles", + "node": "hupWdkj_RtmThGjNUiIt_w", + "reason": { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "org.elasticsearch.server@9.0.0/org.elasticsearch.index.fielddata.ScriptDocValues.throwIfEmpty(ScriptDocValues.java:93)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.index.fielddata.ScriptDocValues$Longs.get(ScriptDocValues.java:117)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.index.fielddata.ScriptDocValues$Longs.getValue(ScriptDocValues.java:112)", + "doc['author_score'].value > 50 ? doc['author_score'].value : 1", + " ^---- HERE" + ], + "script": "doc['author_score'].value > 50 ? doc['author_score'].value : 1", + "lang": "painless", + "position": { + "offset": 19, + "start": 0, + "end": 62 + }, + "caused_by": { + "type": "illegal_state_exception", + "reason": "A document doesn't have a value for a field! Use doc[].size()==0 to check if a document is missing a field!" + } + } + } + ] + }, + "status": 400 +} +``` + +## Problematic code + +```json +{ + "query": { + "function_score": { + "query": { + "match_all": {} + }, + "script_score": { + "script": { + "source": "doc['author_score'].value > 50 ? doc['author_score'].value : 1", + "lang": "painless" + } + } + } + } +} +``` + +## Root cause + +A field not found exception occurs when a script tries to access a field that is not defined in the index mappings. If the field is defined in the mappings but has no value in some documents, the script will not fail as long as you first check whether the field has values. + +For example, calling `doc['author_score'].value` directly on a document that does not contain that field causes an error. The recommended approach is to use `doc[].size()==0` to check if the field is missing in a document before accessing its value. + +## Sample documents + +```json +POST articles/_doc +{ + "title": "Complete Guide to Elasticsearch", + "content": "This is a comprehensive guide...", + "author": "John Doe", + "author_score": 85 +} + +POST articles/_doc +{ + "title": "Basic Query Tutorial", + "content": "Learn the fundamentals...", + "author": "Jane Smith" +} +``` + +## Solution 1: Check field existence before accessing + +Always verify the existence of a field by using `size()` before accessing field values: + +```json +GET articles/_search +{ + "query": { + "function_score": { + "query": { + "match_all": {} + }, + "script_score": { + "script": { + "source": """ + if (doc['author_score'].size() > 0) { + return doc['author_score'].value > 50 ? doc['author_score'].value : 1; + } else { + return 1; + } + """, + "lang": "painless" + } + } + } + } +} +``` + +## Solution 2: New field API approach + +The [field API](/explore-analyze/scripting/script-fields-api.md) provides a more elegant solution that handles missing values automatically, by allowing you to specify default values. This approach is more concise and eliminates the need for explicit field existence checks: + +```json +GET articles/_search +{ + "query": { + "function_score": { + "query": { + "match_all": {} + }, + "script_score": { + "script": { + "source": """ + long authorScore = field('author_score').get(1L); + return authorScore > 50 ? authorScore : 1; + """, + "lang": "painless" + } + } + } + } +} +``` + +## Solution 3: Use the $ shortcut in field API syntax + +With the field API, you can make the solution even more concise using the `$` shortcut: + +```json +GET articles/_search +{ + "query": { + "function_score": { + "query": { + "match_all": {} + }, + "script_score": { + "script": { + "source": """ + long authorScore = $('author_score', 1L); + return authorScore > 50 ? authorScore : 1; + """, + "lang": "painless" + } + } + } + } +} +``` + +## Results + +```json +{ + ..., + "hits": { + ..., + "hits": [ + { + "_index": "articles", + "_id": "pnZXL5kBTbKqUnB52aCH", + "_score": 85, + "_source": { + "title": "Complete Guide to Elasticsearch", + "content": "This is a comprehensive guide...", + "author": "John Doe", + "author_score": 85 + } + }, + { + "_index": "articles", + "_id": "i6hXL5kB0eMypkDY3mQ4", + "_score": 1, + "_source": { + "title": "Basic Query Tutorial", + "content": "Learn the fundamentals...", + "author": "Jane Smith" + } + } + ] + } +} +``` + +## Notes + +* **Field presence:** Always check if fields exist before accessing them, using `.size() > 0`. +* **Document variation:** Not all documents are guaranteed to have the same field structure. +* **Mapping awareness:** A field must be defined in the index mappings to be accessed with doc values, and its value in each document must be validated. +* **Field API:** The `field` API and `$` shortcut handle missing values gracefully, using default values. +* **Compatibility:** Some field types (such as `text` or `geo`) [aren't yet supported](/explore-analyze/scripting/script-fields-api.md#_supported_mapped_field_types) by the field API; continue using `doc` for those. diff --git a/troubleshoot/elasticsearch/painless-ingest-pipeline-failures.md b/troubleshoot/elasticsearch/painless-ingest-pipeline-failures.md new file mode 100644 index 0000000000..193f76340f --- /dev/null +++ b/troubleshoot/elasticsearch/painless-ingest-pipeline-failures.md @@ -0,0 +1,138 @@ +--- +navigation_title: Ingest pipeline failures +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot ingest pipeline failures in Painless + +Follow these guidelines to avoid [ingest pipeline](elasticsearch://reference/scripting-languages/painless/painless-ingest-processor-context.md) errors in your Painless scripts. + +When you convert time strings to nanoseconds in ingest pipelines, attempting to perform arithmetic operations directly on string values without proper date parsing causes type casting errors. + +## Sample error + +```json +{ + "docs": [ + { + "error": { + "root_cause": [ + { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "ctx.nanoseconds = ctx.time_field * 1000000;", + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 32, + "start": 11, + "end": 63 + } + } + ], + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "ctx.nanoseconds = ctx.time_field * 1000000;", + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 32, + "start": 11, + "end": 63 + }, + "caused_by": { + "type": "class_cast_exception", + "reason": "Cannot apply [*] operation to types [java.lang.String] and [java.lang.Integer]." + } + } + } + ] +} +``` + +## Problematic code + +```json +{ + "script": { + "source": """ + ctx.nanoseconds = ctx.time_field * 1000000; + """ + } +} +``` + +## Root cause + +When accessing fields using `ctx.time_field` in ingest pipelines, the values are not automatically parsed to their mapped field types. The script attempts to multiply a string value (time field) directly with an integer. Time strings such as `"00:00:00.022"` remain as strings. They need to be properly parsed as dates and converted to epoch milliseconds before performing arithmetic operations. + +## Solution: Use `SimpleDateFormat` in the script processor + +Parse the time string directly using SimpleDateFormat and get epoch milliseconds: + +```json +POST _ingest/pipeline/_simulate +{ + "pipeline": { + "description": "Parse time and convert to nanoseconds", + "processors": [ + { + "script": { + "source": """ + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); + long millis = sdf.parse(ctx.time_field).getTime(); + ctx.timestamp_nanos = millis * 1000000L; + """ + } + } + ] + }, + "docs": [ + { + "_index": "index", + "_id": "id", + "_source": { + "time_field": "00:00:00.022" + } + } + ] +} +``` + +## Result + +```json +{ + "docs": [ + { + "doc": { + "_index": "index", + "_version": "-3", + "_id": "id", + "_source": { + "time_field": "00:00:00.022", + "timestamp_nanos": 22000000 + }, + "_ingest": { + "timestamp": "2025-09-02T17:40:42.175772728Z" + } + } + } + ] +} +``` + +## Notes + +* Time strings such as `"HH:mm:ss.SSS"` must be explicitly parsed before performaing arithmetic operations. +* Using `SimpleDateFormat` in a script processor allows custom parsing. diff --git a/troubleshoot/elasticsearch/painless-null-pointer-exceptions.md b/troubleshoot/elasticsearch/painless-null-pointer-exceptions.md new file mode 100644 index 0000000000..b57084d231 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-null-pointer-exceptions.md @@ -0,0 +1,164 @@ +--- +navigation_title: Null pointer exceptions +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot null pointer exceptions in Painless + +Follow these guidelines to avoid null pointer exceptions in your Painless scripts. + +In Painless, field access methods vary depending on the script execution [context](elasticsearch://reference/scripting-languages/painless/painless-contexts.md). Using a field access pattern that is not valid for the current script context causes a `null_pointer_exception`. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "params['_source'].tags.size() > 2", + " ^---- HERE" + ], + "script": "params['_source'].tags.size() > 2", + "lang": "painless", + "position": { + "offset": 17, + "start": 0, + "end": 33 + } + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "products", + "node": "hupWdkj_RtmThGjNUiIt_w", + "reason": { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "params['_source'].tags.size() > 2", + " ^---- HERE" + ], + "script": "params['_source'].tags.size() > 2", + "lang": "painless", + "position": { + "offset": 17, + "start": 0, + "end": 33 + }, + "caused_by": { + "type": "null_pointer_exception", + "reason": "cannot access method/field [tags] from a null def reference" + } + } + } + ] + }, + "status": 400 +}á +``` + +### Problematic code + +```json +{ + "query": { + "bool": { + "filter": { + "script": { + "script": { + "source": "params['_source'].tags.size() > 2", + "lang": "painless" + } + } + } + } + } +} +``` + +### Sample data + +```json +POST products/_doc +{ + "name": "Laptop", + "price": 999.99, + "category": "electronics", + "tags": ["premium", "gaming", "portable"] +} +``` + +### Root cause + +A common cause of null pointer exceptions in Painless scripts is attempting to access document fields using incorrect access patterns for the specific [script context](elasticsearch://reference/scripting-languages/painless/painless-contexts.md). To learn more about the context-dependent field access methods in Painless, refer to Painless syntax-context bridge in the Explore and Analyze documentation. +% Link to come after E&A PR is merged. + +The error occurs because `params['_source']` is not available in script filter contexts. In script filters, field values must be accessed through `doc` values, not through the `params['_source']` map. + +### Solution: Use correct field access pattern for context + +For script filter contexts, use `doc` values instead of `params._source`: + +```json +GET products/_search +{ + "query": { + "bool": { + "filter": { + "script": { + "script": { + "source": "doc['tags.keyword'].size() > 2", + "lang": "painless" + } + } + } + } + } +} +``` + +### Results + +```json +{ + ..., + "hits": { + ..., + "hits": [ + { + ..., + "_source": { + "name": "Laptop", + "price": 999.99, + "category": "electronics", + "tags": [ + "premium", + "gaming", + "portable" + ] + } + } + ] + } +} +``` + +### Notes + +* **Context matters:** Always verify the correct field access pattern for your script context. +* **Context limitations:** Script filters cannot access `params['_source']` . +* **Field mapping:** Use `.keyword` suffix for text fields when accessing via doc values. + diff --git a/troubleshoot/elasticsearch/painless-regex-pattern-matching-failures.md b/troubleshoot/elasticsearch/painless-regex-pattern-matching-failures.md new file mode 100644 index 0000000000..f09c92a537 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-regex-pattern-matching-failures.md @@ -0,0 +1,119 @@ +--- +navigation_title: Regex pattern matching failures +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot regex pattern matching failures in Painless + +Follow these guidelines to avoid [regex](elasticsearch://reference/scripting-languages/painless/painless-regexes.md) operation errors in your Painless scripts. + +Regex operations in Painless can fail for several reasons: the regex feature is disabled, there is incorrect matcher usage, or there are malformed regex patterns. This may occur when standard Java regex behavior is thought to apply directly to Painless. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "java.base/java.util.regex.Matcher.checkMatch(Matcher.java:1850)", + "java.base/java.util.regex.Matcher.group(Matcher.java:685)", + """return /(?<=SHIPPER:).*?(?=\\n)/.matcher(orderLog).group(0); + """, + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 176, + "start": 126, + "end": 191 + } + } + ], + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "java.base/java.util.regex.Matcher.checkMatch(Matcher.java:1850)", + "java.base/java.util.regex.Matcher.group(Matcher.java:685)", + """return /(?<=SHIPPER:).*?(?=\\n)/.matcher(orderLog).group(0); + """, + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 176, + "start": 126, + "end": 191 + }, + "caused_by": { + "type": "illegal_state_exception", + "reason": "No match found" + } + }, + "status": 400 +} +``` + +## Problematic code + +```json +{ + "script": { + "lang": "painless", + "source": """ + String orderLog = "Order processing initiated\\nSHIPPER: SHIP-EX EXPRESS DELIVERY\\nTracking number generated"; + + return /(?<=SHIPPER:).*?(?=\\n)/.matcher(orderLog).group(0); + """ + } +} +``` + +## Root cause + +The error occurs because the `group()` method is called on a Matcher before calling `find()`. You must explicitly search for matches before accessing groups. This is a common mistake when developers expect the matcher to automatically find matches. + +## Solution: Call find() before accessing groups + +Always call `find()` before using `group()` methods: + +```json +POST _scripts/painless/_execute +{ + "script": { + "lang": "painless", + "source": """ + String orderLog = "Order processing initiated\\nSHIPPER: SHIP-EX EXPRESS DELIVERY\\nTracking number generated"; + + Matcher m = /(?<=SHIPPER:).*?(?=\\n)/.matcher(orderLog); + boolean found = m.find(); + + return found ? m.group(0).trim() : "No match"; + """ + } +} +``` + +## Results + +```json +{ + "result": "SHIP-EX EXPRESS DELIVERY" +} +``` + +## Notes + +* **Call find() first:** Always use `matcher.find()` before accessing groups. +* **Enable regex:** Set `script.painless.regex.enabled=true` in the `elasticsearch.yml` settings file if regex is disabled. +* **Group numbering:** Use `group(0)` for the entire match, `group(1)` for first capture group, and so on. +* **Performance impact:** Regex operations can be expensive, especially with complex patterns. diff --git a/troubleshoot/elasticsearch/painless-runtime-field-exceptions.md b/troubleshoot/elasticsearch/painless-runtime-field-exceptions.md new file mode 100644 index 0000000000..262740715c --- /dev/null +++ b/troubleshoot/elasticsearch/painless-runtime-field-exceptions.md @@ -0,0 +1,163 @@ +--- +navigation_title: Runtime field exceptions +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot runtime field exceptions in Painless + +Follow these guidelines to avoid [runtime field](elasticsearch://reference/scripting-languages/painless/painless-runtime-fields-context.md) exceptions in your Painless scripts. + +## Using `return` instead of `emit` in runtime field in runtime mappings + +When you create runtime mappings in a Painless script, using `return` instead of `emit` to output values leads to compilation errors, as runtime field scripts require the `emit()` function to produce field values. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "class_cast_exception", + "reason": "class_cast_exception: Cannot cast from [java.lang.String] to [void]." + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "users", + "node": "hupWdkj_RtmThGjNUiIt_w", + "reason": { + "type": "script_exception", + "reason": "compile error", + "script_stack": [ + """... Doe"; + + return firstName + " " + lastNam ...""", + " ^---- HERE" + ], + "script": """ + String firstName = "John"; + String lastName = "Doe"; + + return firstName + " " + lastName; + """, + "lang": "painless", + "position": { + "offset": 92, + "start": 67, + "end": 117 + }, + "caused_by": { + "type": "class_cast_exception", + "reason": "class_cast_exception: Cannot cast from [java.lang.String] to [void]." + } + } + } + ], + "caused_by": { + "type": "class_cast_exception", + "reason": "class_cast_exception: Cannot cast from [java.lang.String] to [void]." + } + }, + "status": 400 +} +``` + +## Problematic code + +```json +{ + "runtime_mappings": { + "full_name": { + "type": "keyword", + "script": { + "source": """ + String firstName = "John"; + String lastName = "Doe"; + + return firstName + " " + lastName; + """ + } + } + } +} +``` + +## Root cause + +Runtime field scripts use `emit()` to produce values, rather than `return`. The `emit()` function is specifically designed for runtime mappings to output field values, while `return` is used in other Painless contexts such as script queries or update scripts. The error occurs because runtime field scripts expect a void return type, but the script attempts to return a String value. + +## Solution: Replace return with emit + +Replace `return` statements with `emit()` calls: + +```json +POST users/_search +{ + "fields": [ + { + "field": "*", + "include_unmapped": "true" + } + ], + "runtime_mappings": { + "full_name": { + "type": "keyword", + "script": { + "source": """ + String firstName = "John"; + String lastName = "Doe"; + + emit(firstName + " " + lastName); + """ + } + } + } +} +``` + +## Results + +```json +{ + ..., + "hits": { + ..., + "hits": [ + { + ..., + "_source": { + "user": { + "name": "Jane Doe" + } + }, + "fields": { + "user.name.keyword": [ + "Jane Doe" + ], + "user_info": [ + "incomplete: Jane Doe" + ], + "user.name": [ + "Jane Doe" + ] + } + } + ] + } +} +``` + +## Notes + +* Runtime field scripts must use `emit()` to output values, not `return`. +* `emit()` can be called multiple times in a script to emit multiple values. diff --git a/troubleshoot/elasticsearch/painless-sandbox-limitations.md b/troubleshoot/elasticsearch/painless-sandbox-limitations.md new file mode 100644 index 0000000000..d76497f7c2 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-sandbox-limitations.md @@ -0,0 +1,139 @@ +--- +navigation_title: Sandbox limitations +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot sandbox limitations in Painless + +Follow these guidelines to avoid Painless sandbox restriction errors in your script. + +Painless implements sandbox limitations that differ from standard Java syntax and behavior. These restrictions are designed to optimize performance and prevent logical errors, which can lead to unexpected compilation errors when common Java patterns are used. One limitation is that empty `foreach` loops are not allowed. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "script_exception", + "reason": "compile error", + "script_stack": [ + """... keyboard": 85]; + + for (item in products.ent ...""", + " ^---- HERE" + ], + "script": """ + Map products = ["laptop": 1200, "mouse": 25, "keyboard": 85]; + + for (item in products.entrySet()) + { + + + } + + return false; + """, + "lang": "painless", + "position": { + "offset": 80, + "start": 55, + "end": 105 + } + } + ], + "type": "script_exception", + "reason": "compile error", + "script_stack": [ + """... keyboard": 85]; + + for (item in products.ent ...""", + " ^---- HERE" + ], + "script": """ + Map products = ["laptop": 1200, "mouse": 25, "keyboard": 85]; + + for (item in products.entrySet()) + { + + + } + + return false; + """, + "lang": "painless", + "position": { + "offset": 80, + "start": 55, + "end": 105 + }, + "caused_by": { + "type": "illegal_argument_exception", + "reason": "extraneous foreach loop" + } + }, + "status": 400 +} +``` + +## Problematic code + +```json +{ + "script": { + "lang": "painless", + "source": """ + Map products = ["laptop": 1200, "mouse": 25, "keyboard": 85]; + + for (item in products.entrySet()) + { + + + } + + return false; + """ + } +} +``` + +## Root cause + +* **Empty loop blocks:** Painless does not allow empty `foreach` loops as an intentional feature to enhance usability and performance. +* **Performance optimization:** Since scripts run once per document and there may be millions of documents, empty loops are considered wasteful. + +The error occurs because Painless expects meaningful code inside loop blocks and treats empty loops as potential bugs or risks. + +### Solution: Add code inside loop blocks + +Always include actual code inside foreach loops. For example: + +```json +POST _scripts/painless/_execute +{ + "script": { + "lang": "painless", + "source": """ + Map products = ["laptop": 1200, "mouse": 25, "keyboard": 85]; + String inventory = ""; + + for (item in products.entrySet()) + { + inventory += item.getKey() + " costs $" + item.getValue() + ". "; + } + + return inventory; + """ + } +} +``` + +## Notes + +* **Empty loops:** Painless intentionally prohibits empty `foreach` loops for performance reasons. +* **Syntax differences:** Painless syntax differs from Java in several ways to optimize execution and prevent logical errors. diff --git a/troubleshoot/elasticsearch/painless-script-score-calculation-errors.md b/troubleshoot/elasticsearch/painless-script-score-calculation-errors.md new file mode 100644 index 0000000000..44d3a66665 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-script-score-calculation-errors.md @@ -0,0 +1,188 @@ +--- +navigation_title: Script score calculation errors +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot script score calculation errors in Painless + +Follow these guidelines to avoid scoring calculation errors in your Painless scripts. + +When you use [`script_score`](elasticsearch://reference/query-languages/query-dsl/query-dsl-script-score-query.md) with type `double`, + the script can return unexpected null values, negative values `0.0`, or Infinity, causing documents to receive a score of `0` or be excluded from results entirely. This commonly occurs when field access patterns don't account for missing values or when mathematical operations result in null propagation. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "illegal_argument_exception", + "reason": "script_score script returned an invalid score [-Infinity] for doc [0]. Must be a non-negative score!" + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "products", + "node": "CxMTEjvKSEC0k0aTr4OM3A", + "reason": { + "type": "illegal_argument_exception", + "reason": "script_score script returned an invalid score [-Infinity] for doc [0]. Must be a non-negative score!" + } + } + ], + "caused_by": { + "type": "illegal_argument_exception", + "reason": "script_score script returned an invalid score [-Infinity] for doc [0]. Must be a non-negative score!", + "caused_by": { + "type": "illegal_argument_exception", + "reason": "script_score script returned an invalid score [-Infinity] for doc [0]. Must be a non-negative score!" + } + } + }, + "status": 400 +} +``` + +## Problematic code + +```json +{ + "query": { + "script_score": { + "query": { + "match_all": {} + }, + "script": { + "lang": "painless", + "source": """ + double price = 0.0; // Simulating problematic calculation + double rating = 5.0; + + return Math.log(price) * rating; + """ + } + } + } +} +``` + +## Root cause + +The error occurs because of mathematical edge cases in calculations: + +1. **Math.log() with zero or negative values:** `Math.log(0)` returns negative infinity, `Math.log(-x)` returns `NaN`. +2. **Division by zero:** Operations such as `x/0` throw an `arithmetic_exception`. +3. **NaN propagation:** Any mathematical operation involving `NaN` results in `NaN`. +4. **Infinity calculations:** Operations with infinity often result in `NaN` or unexpected values. + +When a script returns `NaN`, negative infinity, or other invalid numbers, Painless converts the score to 0.0, causing unexpected ranking behavior. + +### Solution: Add mathematical safety checks + +Always validate mathematical inputs and handle edge cases: + +```json +GET products/_search +{ + "query": { + "script_score": { + "query": { + "match_all": {} + }, + "script": { + "lang": "painless", + "source": """ + double price = 0.0; + double rating = 5.0; + + double safePrice = Math.max(price, 1.0); // Ensure > 0 for log + + // Calculate score with safety checks + double logPrice = Math.log(safePrice); + double score = logPrice * rating; + + // Handle NaN or infinity results + if (Double.isNaN(score) || Double.isInfinite(score)) { + return 1.0; + } + + return Math.max(score, 0.1); // Ensure a minimum positive score + """ + } + } + } +} +``` + +## Sample documents + +```json +POST products/_doc +{ + "name": "Premium Laptop", + "price": 999.99, + "rating": 4.7, + "category": "electronics" +} + +POST products/_doc +{ + "name": "Free Software", + "price": 0, + "rating": 5.0, + "category": "software" +} +``` + +## Results + +```json +{ + ..., + "hits": { + ..., + "hits": [ + { + "_index": "products", + "_id": "j6gZNZkB0eMypkDYmmSC", + "_score": 0.1, + "_source": { + "name": "Premium Laptop", + "price": 999.99, + "rating": 4.7, + "category": "electronics" + } + }, + { + "_index": "products", + "_id": "kKgZNZkB0eMypkDYn2SP", + "_score": 0.1, + "_source": { + "name": "Free Software", + "price": 0, + "rating": 5, + "category": "software" + } + } + ] + } +} +``` + +## Notes + +* **Mathematical safety:** Validate inputs for functions like `Math.log()`. +* **Default values:** Provide meaningful defaults for missing fields to maintain consistent scoring. +* **Minimum scores:** Ensure scripts return positive values to avoid zero scores. +* **Null handling:** Mathematical operations with null values propagate null throughout the calculation. + diff --git a/troubleshoot/elasticsearch/painless-scripting.md b/troubleshoot/elasticsearch/painless-scripting.md new file mode 100644 index 0000000000..7b46c1ff83 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-scripting.md @@ -0,0 +1,28 @@ +--- +navigation_title: Painless scripting +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot Painless scripting in {{es}} + +Use the topics in this section to troubleshoot common errors in your [Painless](/explore-analyze/scripting/modules-scripting-painless.md) scripts. + +* [Array/list manipulation errors](/troubleshoot/elasticsearch/painless-array-list-manipulation-errors.md) +* [Date math errors](/troubleshoot/elasticsearch/painless-date-math-errors.md) +* [Field not found (mapping conflicts)](/troubleshoot/elasticsearch/painless-field-not-found.md) +* [Ingest pipeline failures](/troubleshoot/elasticsearch/painless-ingest-pipeline-failures.md) +* [Null pointer exceptions](/troubleshoot/elasticsearch/painless-null-pointer-exceptions.md) +* [Regex pattern matching failures](/troubleshoot/elasticsearch/painless-regex-pattern-matching-failures.md) +* [Runtime field exceptions](/troubleshoot/elasticsearch/painless-runtime-field-exceptions.md) +* [Sandbox limitations](/troubleshoot/elasticsearch/painless-sandbox-limitations.md) +* [Script score calculation errors](/troubleshoot/elasticsearch/painless-script-score-calculation-errors.md) +* [Subfield access](/troubleshoot/elasticsearch/painless-subfield-access.md) +* [Type casting issues](/troubleshoot/elasticsearch/painless-type-casting-issues.md) + +## Additional resources +* [Troubleshooting overview](/troubleshoot/index.md) +* [Contact us](/troubleshoot/index.md#contact-us) \ No newline at end of file diff --git a/troubleshoot/elasticsearch/painless-subfield-access.md b/troubleshoot/elasticsearch/painless-subfield-access.md new file mode 100644 index 0000000000..b3d85a76d6 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-subfield-access.md @@ -0,0 +1,286 @@ +--- +navigation_title: Subfield access +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot subfield access in Painless + +Follow these guidelines to avoid nested field access errors in your Painless scripts. + +When you access subfields using the doc accessor, using incorrect syntax or trying to access non-existent fields without proper validation leads to runtime errors. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "org.elasticsearch.server@9.0.0/org.elasticsearch.search.lookup.LeafDocLookup.getFactoryForDoc(LeafDocLookup.java:146)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:186)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:33)", + """start = doc['event'].start.value; + def """, + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 95, + "start": 83, + "end": 131 + } + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "events", + "node": "CxMTEjvKSEC0k0aTr4OM3A", + "reason": { + "type": "script_exception", + "reason": "runtime error", + "script_stack": [ + "org.elasticsearch.server@9.0.0/org.elasticsearch.search.lookup.LeafDocLookup.getFactoryForDoc(LeafDocLookup.java:146)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:186)", + "org.elasticsearch.server@9.0.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:33)", + """start = doc['event'].start.value; + def """, + " ^---- HERE" + ], + "script": " ...", + "lang": "painless", + "position": { + "offset": 95, + "start": 83, + "end": 131 + }, + "caused_by": { + "type": "illegal_argument_exception", + "reason": "No field found for [event] in mapping" + } + } + } + ] + }, + "status": 400 +} +``` + +## Problematic approaches + +The following two methods of accessing subfields will result in errors: + +```json +{ + "runtime_mappings": { + "event_duration_match": { + "type": "boolean", + "script": { + "source": """ + // Incorrect approach 1: trying to access nested property + def start = doc['event'].start.value; + def end = doc['event'].end.value; + emit(start == end); + """ + } + } + } +} +``` + +Or: + +```json +{ + "runtime_mappings": { + "event_duration_match": { + "type": "boolean", + "script": { + "source": """ + // Incorrect approach 2: using bracket notation incorrectly + def start = doc['event']['start'].value; + def end = doc['event']['end'].value; + emit(start == end); + """ + } + } + } +} +``` + +## Sample data + +```json +PUT events/_doc/1 +{ + "title": "Conference Call", + "event": { + "start": "2024-01-15T09:00:00Z", + "end": "2024-01-15T10:00:00Z" + } +} + +PUT events/_doc/2 +{ + "title": "Team Meeting", + "event": { + "start": "2024-01-15T14:00:00Z", + "end": "2024-01-15T14:00:00Z" + } +} + +PUT events/_doc/3 +{ + "title": "Quick Update" +} +``` + +## Root cause + +When accessing subfields in Painless scripts using the `doc` accessor, the correct syntax requires using the full dot notation path as a single string (`doc['parent.child’]`), rather than as a separate property access or bracket notation. This applies to all Painless contexts where `doc` accessor is available, including runtime mapping, script queries, aggregations, and ingest processors. + +The error occurs because `doc[‘event’]` attempts to find a field named “event” rather than accessing subfields within the event object. + +## Solution: Correct subfield access with validation + +Use full dot notation and validate that the field exists:: + +```json +POST events/_search +{ + "fields": [ + { + "field": "*", + "include_unmapped": "true" + } + ], + "runtime_mappings": { + "event_duration_match": { + "type": "boolean", + "script": { + "source": """ + if (doc.containsKey('event.start') && doc.containsKey('event.end') && doc['event.start'].size() > 0 && doc['event.end'].size() > 0) { + + def start = doc['event.start'].value; + def end = doc['event.end'].value; + emit(start == end); + } else { + emit(false); + } + """ + } + } + } +} +``` + +## Results + +```json +{ + ..., + "hits": { + ..., + "hits": [ + { + ..., + "_source": { + "title": "Conference Call", + "event": { + "start": "2024-01-15T09:00:00Z", + "end": "2024-01-15T10:00:00Z" + } + }, + "fields": { + "title.keyword": [ + "Conference Call" + ], + "event_duration_match": [ + false + ], + "title": [ + "Conference Call" + ], + "event": [ + { + "start": [ + "2024-01-15T09:00:00.000Z" + ], + "end": [ + "2024-01-15T10:00:00.000Z" + ] + } + ] + } + }, + { + ..., + "_source": { + "title": "Team Meeting", + "event": { + "start": "2024-01-15T14:00:00Z", + "end": "2024-01-15T14:00:00Z" + } + }, + "fields": { + "title.keyword": [ + "Team Meeting" + ], + "event_duration_match": [ + false + ], + "title": [ + "Team Meeting" + ], + "event": [ + { + "start": [ + "2024-01-15T14:00:00.000Z" + ], + "end": [ + "2024-01-15T14:00:00.000Z" + ] + } + ] + } + }, + { + ..., + "_source": { + "title": "Quick Update" + }, + "fields": { + "title.keyword": [ + "Quick Update" + ], + "title": [ + "Quick Update" + ], + "event_duration_match": [ + false + ] + } + } + ] + } +} +``` + +## Notes + +* Use full dot notation as a single string: `doc['parent.child’]` rather than `doc['parent.['child’].` +* Always validate field existence using `.size() > 0` before accessing subfield values. +* Field validation is crucial when documents have varying object structures. + diff --git a/troubleshoot/elasticsearch/painless-type-casting-issues.md b/troubleshoot/elasticsearch/painless-type-casting-issues.md new file mode 100644 index 0000000000..1ba430ed49 --- /dev/null +++ b/troubleshoot/elasticsearch/painless-type-casting-issues.md @@ -0,0 +1,215 @@ +--- +navigation_title: Type casting issues +applies_to: + stack: ga + serverless: ga +products: + - id: elasticsearch +--- + +# Troubleshoot type casting issues in Painless + +Follow these guidelines to avoid type conversion errors in your Painless scripts. + +When you perform [type casting](elasticsearch://reference/scripting-languages/painless/painless-casting.md) in Painless scripts, attempting implicit conversions or casting incompatible types without proper validation leads to compilation and runtime errors. + +## Sample error + +```json +{ + "error": { + "root_cause": [ + { + "type": "class_cast_exception", + "reason": "class_cast_exception: Cannot cast from [double] to [int]." + } + ], + "type": "search_phase_execution_exception", + "reason": "all shards failed", + "phase": "query", + "grouped": true, + "failed_shards": [ + { + "shard": 0, + "index": "products", + "node": "CxMTEjvKSEC0k0aTr4OM3A", + "reason": { + "type": "script_exception", + "reason": "compile error", + "script_stack": [ + "... int discountedPrice = price * discount; // Err ...", + " ^---- HERE" + ], + "script": """ + double price = doc['price'].value; + double discount = 0.85; + int discountedPrice = price * discount; // Error: implicit cast + emit(discountedPrice); + """, + "lang": "painless", + "position": { + "offset": 112, + "start": 87, + "end": 137 + }, + "caused_by": { + "type": "class_cast_exception", + "reason": "class_cast_exception: Cannot cast from [double] to [int]." + } + } + } + ], + "caused_by": { + "type": "class_cast_exception", + "reason": "class_cast_exception: Cannot cast from [double] to [int]." + } + }, + "status": 400 +} +``` + +## Problematic code + +```json +{ + "runtime_mappings": { + "discounted_price": { + "type": "long", + "script": { + "source": """ + double price = doc['price'].value; + double discount = 0.85; + int discountedPrice = price * discount; // Error: implicit cast + emit(discountedPrice); + """ + } + } + } +} +``` + +## Sample data + +```json +PUT products/_doc/1 +{ + "name": "Laptop", + "price": 999.99 +} + +PUT products/_doc/2 +{ + "name": "Mouse", + "price": 25 +} +``` + +## Root cause + +Painless has [specific rules](https://www.elastic.co/docs/reference/scripting-languages/painless/painless-casting) for implicit and explicit type casting between numeric types. While some numeric conversions are allowed implicitly (widening conversions such as `int` into `double`), narrowing conversions such as `double` to `int` require explicit casting. The script attempts to assign a `double` result to an `int` variable without explicit casting, which causes a compilation error since this is a narrowing conversion that could lose precision. Additionally, field values may be null or of unexpected types, requiring proper validation before casting. + +## Solution: Use explicit numeric type casting + +Use explicit casting with proper validation: + +```json +POST products/_search +{ + "fields": [ + { + "field": "*", + "include_unmapped": "true" + } + ], + "runtime_mappings": { + "discounted_price": { + "type": "long", + "script": { + "source": """ + if (doc['price'].size() > 0) { + def value = doc['price'].value; + + if (value != null && value instanceof Number) { + double price = (double) value; + double discount = 0.85; + long discountedPrice = (long)(price * discount); + emit(discountedPrice); + } else { + emit(0L); + } + } else { + emit(0L); + } + """ + } + } + } +} +``` + +## Results + +```json +{ + ..., + "hits": { + ..., + "hits": [ + { + "_index": "products", + "_id": "1", + "_score": 1, + "_source": { + "name": "Laptop", + "price": 999.99 + }, + "fields": { + "name": [ + "Laptop" + ], + "name.keyword": [ + "Laptop" + ], + "price": [ + 999.99 + ], + "discounted_price": [ + 849 + ] + } + }, + { + "_index": "products", + "_id": "2", + "_score": 1, + "_source": { + "name": "Mouse", + "price": 25 + }, + "fields": { + "name": [ + "Mouse" + ], + "name.keyword": [ + "Mouse" + ], + "price": [ + 25 + ], + "discounted_price": [ + 21 + ] + } + } + ] + } +} +``` + +## Notes + +* Validate the existence of a field using `.size() > 0` before accessing values. +* Check for null values using `value != null` before casting. +* Use `instanceof Number` to verify the value is numeric before casting. +* Handle missing fields gracefully with default values. +* When working with {{es}} field values, always validate the actual field type. diff --git a/troubleshoot/toc.yml b/troubleshoot/toc.yml index 7ed894a8bb..c464c1786e 100644 --- a/troubleshoot/toc.yml +++ b/troubleshoot/toc.yml @@ -51,6 +51,19 @@ toc: - file: elasticsearch/diagnosing-unknown-repositories.md - file: elasticsearch/diagnosing-invalid-repositories.md - file: elasticsearch/repeated-snapshot-failures.md + - file: elasticsearch/painless-scripting.md + children: + - file: elasticsearch/painless-array-list-manipulation-errors.md + - file: elasticsearch/painless-date-math-errors.md + - file: elasticsearch/painless-field-not-found.md + - file: elasticsearch/painless-ingest-pipeline-failures.md + - file: elasticsearch/painless-null-pointer-exceptions.md + - file: elasticsearch/painless-regex-pattern-matching-failures.md + - file: elasticsearch/painless-runtime-field-exceptions.md + - file: elasticsearch/painless-sandbox-limitations.md + - file: elasticsearch/painless-script-score-calculation-errors.md + - file: elasticsearch/painless-subfield-access.md + - file: elasticsearch/painless-type-casting-issues.md - file: elasticsearch/troubleshooting-searches.md - file: elasticsearch/start-ilm.md - file: elasticsearch/index-lifecycle-management-errors.md