elastic/elasticsearch · error · ClassCastException

Cannot apply [-] operation to types [{}] and [{}].

Error message

Cannot apply [-] operation to types [{}] and [{}].

What it means

Thrown by DefMath.sub(Object, Object) when neither operand matches a supported subtraction combination. The Object overload checks instanceof Number and instanceof Character for both sides; if no branch matches (e.g., String, List, Map, or a Number paired with a non-numeric/non-character type), execution falls through to this ClassCastException. This only occurs with def-typed operands, since typed operands are resolved at compile time.

Source

Thrown at modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java:497

                }
            }
        } else if (left instanceof Character) {
            if (right instanceof Number) {
                if (right instanceof Double) {
                    return (char) left - ((Number) right).doubleValue();
                } else if (right instanceof Long) {
                    return (char) left - ((Number) right).longValue();
                } else if (right instanceof Float) {
                    return (char) left - ((Number) right).floatValue();
                } else {
                    return (char) left - ((Number) right).intValue();
                }
            } else if (right instanceof Character) {
                return (char) left - (char) right;
            }
        }

        throw new ClassCastException(
            "Cannot apply [-] operation to types "
                + "["
                + left.getClass().getCanonicalName()
                + "] and ["
                + right.getClass().getCanonicalName()
                + "]."
        );
    }

    // eq: applicable to any arbitrary type, including nulls for both arguments!!!

    private static boolean eq(int a, int b) {
        return a == b;
    }

    private static boolean eq(long a, long b) {
        return a == b;
    }

View on GitHub (pinned to db6a809a66)

Solutions

  1. Declare both operands with an explicit numeric type (int, long, float, double) so the compiler rejects incompatible types at parse time
  2. Add an instanceof Number guard before subtracting: if (a instanceof Number && b instanceof Number) { return ((Number)a).doubleValue() - ((Number)b).doubleValue(); }
  3. Fix the index mapping so the field is consistently typed as a numeric type (integer, float, etc.) across all documents
  4. Use doc['field'].value with a typed receiver instead of def, so the script fails early on type mismatch rather than at the subtraction site

Example fix

// before
def a = doc['amount'].value;
def b = doc['offset'].value;
def result = a - b;

// after
double a = doc['amount'].value;
double b = doc['offset'].value;
double result = a - b;
Defensive patterns

Strategy: type-guard

Validate before calling

// Before subtraction, verify both def values are numeric
def a = params['x'];
def b = params['y'];
if (a instanceof Number && b instanceof Number) {
  return ((Number) a).doubleValue() - ((Number) b).doubleValue();
} else {
  return 0.0; // or throw a descriptive error
}

Type guard

// Check if a def value is safe for arithmetic
boolean isNumeric(def v) {
  return v instanceof Number;
}

Prevention

When it happens

Trigger: A Painless script subtracts two def-typed values where at least one evaluates at runtime to a non-numeric, non-character type (String, List, Map, null). The compiler emits a call to sub(Object, Object) because at least one operand is def; runtime instanceof dispatch finds no matching branch.

Common situations: Field mapped as keyword/text but script assumes numeric; heterogeneous documents where a field is sometimes numeric and sometimes string; def variable assigned from doc['field'].value where the field type varies across indices; subtracting a value parsed from a text-analyzed field.

Related errors


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