elastic/elasticsearch · error · ScriptException

link error

Error message

link error

What it means

Thrown by convertToScriptException with message "link error" during the variable binding phase in newSortScript, newScoreScript, newAggregationScript, newFieldScript, newTermsSetQueryScript. After compilation succeeds, each variable in the expression is bound to a DoubleValuesSource; if binding fails (field doesn't exist, wrong type, param not numeric), the exception is caught and re-thrown as a ScriptException with "link error" and the offending variable name as the stack portion.

Source

Thrown at modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java:396

        return new ExpressionScoreScript(expr, bindings, needsScores);
    }

    /**
     * converts a ParseException at compile-time or link-time to a ScriptException
     */
    private static ScriptException convertToScriptException(String message, String source, String portion, Throwable cause) {
        List<String> stack = new ArrayList<>();
        stack.add(portion);
        StringBuilder pointer = new StringBuilder();
        if (cause instanceof ParseException) {
            int offset = ((ParseException) cause).getErrorOffset();
            for (int i = 0; i < offset; i++) {
                pointer.append(' ');
            }
        }
        pointer.append("^---- HERE");
        stack.add(pointer.toString());
        throw new ScriptException(message, cause, stack, source, NAME);
    }

    private static DoubleValuesSource getDocValueSource(String variable, SearchLookup lookup) throws ParseException {
        VariableContext[] parts = VariableContext.parse(variable);
        if (parts[0].text.equals("doc") == false) {
            throw new ParseException("Unknown variable [" + parts[0].text + "]", 0);
        }
        if (parts.length < 2 || parts[1].type != VariableContext.Type.STR_INDEX) {
            throw new ParseException("Variable 'doc' must be used with a specific field like: doc['myfield']", 3);
        }

        // .value is the default for doc['field'], its optional.
        String variablename = "value";
        String methodname = null;
        if (parts.length == 3) {
            if (parts[2].type == VariableContext.Type.METHOD) {
                methodname = parts[2].text;
            } else if (parts[2].type == VariableContext.Type.MEMBER) {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Read the ScriptException stack — the portion line shows which variable failed to bind.
  2. Verify the field exists in the index mapping: GET <index>/_mapping.
  3. Confirm the field type is numeric (integer, long, float, double, scaled_float), date, or geo_point — text and keyword are not supported.
  4. If the variable is a param, ensure its value is numeric (see error 1237).

Example fix

// before: field 'price' does not exist or is type text
"source": "doc['price'].value"
// after: verify mapping and use correct field
GET my-index/_mapping
// if field is missing, add it or use the correct name
"source": "doc['amount'].value"
Defensive patterns

Strategy: validation

Validate before calling

// Before using an expression, validate that all doc['field'] references exist and are the right type
GET my-index/_mapping
// For each doc['fieldname'] in the expression, confirm:
//   1. fieldname exists in the mapping
//   2. field type is numeric, date, or geo_point
//   3. doc_values is not disabled

Try / catch

try {
    engine.compile(scriptName, scriptSource, context, params);
} catch (ScriptException e) {
    if ("link error".equals(e.getScriptStack().get(0)) || e.getMessage().contains("link error")) {
        // The portion line in the stack names the variable that failed to bind
        logger.error("Expression link error for variable: {}", e.getScriptStack());
        // Check mapping, field type, and param types
    }
    throw e;
}

Prevention

When it happens

Trigger: Expression compiles successfully but references a doc['fieldname'] where 'fieldname' does not exist in the mapping, is not numeric/date/geo, or a param value is non-numeric. Example: expression "doc['nonexistent'].value" passes parsing but fails at binding time.

Common situations: Field renamed or removed from mapping without updating scripts; typo in field name inside the expression; field exists but is of type text/keyword (not supported by expressions); param value type mismatch.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/57ebd7ca1b552b4b. Report an issue: GitHub.