elastic/elasticsearch · error · ClassCastException

Cannot convert [{}] to a Number

Error message

Cannot convert [{}] to a Number

What it means

DefMath.getNumber throws this ClassCastException when dynamicCast (the slow runtime cast used by the def path for compound assignment and explicit casts to a numeric wrapper) receives an object that is neither a Number nor a Character. getNumber is the bridge that converts a dynamic value to Integer/Long/Double/etc. before invoking intValue()/longValue()/doubleValue(). If the object is, say, a String or a List, there is no numeric conversion and the cast cannot proceed.

Source

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

            } else if (clazz == Byte.class) {
                return getNumber(value).byteValue();
            } else if (clazz == Character.class) {
                return (char) getNumber(value).intValue();
            }
            return clazz.cast(value);
        } else {
            return value;
        }
    }

    /** Slowly returns a Number for o. Just for supporting dynamicCast */
    static Number getNumber(Object o) {
        if (o instanceof Number) {
            return (Number) o;
        } else if (o instanceof Character) {
            return Integer.valueOf((char) o);
        } else {
            throw new ClassCastException("Cannot convert [" + o.getClass() + "] to a Number");
        }
    }

    private static final MethodHandle DYNAMIC_CAST;
    private static final MethodHandle DYNAMIC_RECEIVER_CAST;
    static {
        final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
        try {
            DYNAMIC_CAST = methodHandlesLookup.findStatic(
                methodHandlesLookup.lookupClass(),
                "dynamicCast",
                MethodType.methodType(Object.class, Class.class, Object.class)
            );
            DYNAMIC_RECEIVER_CAST = methodHandlesLookup.findStatic(
                methodHandlesLookup.lookupClass(),
                "dynamicReceiverCast",
                MethodType.methodType(Object.class, Object.class, Object.class)
            );

View on GitHub (pinned to db6a809a66)

Solutions

  1. Validate or parse the value before the numeric cast: use Integer.parseInt or Double.parseDouble for strings, or check the field mapping.
  2. Avoid def for values that may be non-numeric; use explicit types so the compiler forces consistency.
  3. If the field can hold mixed types, add a runtime type check (instanceof Number) before the cast.

Example fix

// before
def x = doc['code'].value;  // keyword field, a String
def total = 0;
total += x;  // getNumber throws
// after
String x = doc['code'].value;
int parsed = Integer.parseInt(x);
int total = 0;
total += parsed;
Defensive patterns

Strategy: validation

Validate before calling

// Check the value is a Number or Character before numeric cast:
// def x = doc['code'].value;
// if (x instanceof Number || x instanceof Character) { int i = ((Number)x).intValue(); } else { /* handle */ }

Type guard

// boolean isNumeric = x instanceof Number || x instanceof Character;

Prevention

When it happens

Trigger: A def-typed compound assignment or explicit numeric cast where the RHS resolves at runtime to a non-numeric object: 'def x = 'abc'; int i = (int) x;' or 'def x = '5'; def y = 0; y += x;'. dynamicCast calls getNumber, which throws because String is not a Number or Character.

Common situations: Scripts that read def values from heterogeneous sources (JSON, doc fields of keyword/text type) and attempt arithmetic casts without validation. Compound assignments (+=, -=) on def variables where the source data is occasionally a string. Migrating scripts that worked on integer fields to keyword fields.

Related errors


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