{"record":{"id":"78a2405719d8a237","repo":"elastic/elasticsearch","slug":"cannot-apply-operation-to-types-and-78a240","errorCode":null,"errorMessage":"Cannot apply [-] operation to types [{}] and [{}].","messagePattern":"Cannot apply \\[-\\] operation to types \\[(.+?)\\] and \\[(.+?)\\]\\.","errorType":"exception","errorClass":"ClassCastException","httpStatus":null,"severity":"error","filePath":"modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java","lineNumber":497,"sourceCode":"                }\n            }\n        } else if (left instanceof Character) {\n            if (right instanceof Number) {\n                if (right instanceof Double) {\n                    return (char) left - ((Number) right).doubleValue();\n                } else if (right instanceof Long) {\n                    return (char) left - ((Number) right).longValue();\n                } else if (right instanceof Float) {\n                    return (char) left - ((Number) right).floatValue();\n                } else {\n                    return (char) left - ((Number) right).intValue();\n                }\n            } else if (right instanceof Character) {\n                return (char) left - (char) right;\n            }\n        }\n\n        throw new ClassCastException(\n            \"Cannot apply [-] operation to types \"\n                + \"[\"\n                + left.getClass().getCanonicalName()\n                + \"] and [\"\n                + right.getClass().getCanonicalName()\n                + \"].\"\n        );\n    }\n\n    // eq: applicable to any arbitrary type, including nulls for both arguments!!!\n\n    private static boolean eq(int a, int b) {\n        return a == b;\n    }\n\n    private static boolean eq(long a, long b) {\n        return a == b;\n    }","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java#L479-L515","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Declare both operands with an explicit numeric type (int, long, float, double) so the compiler rejects incompatible types at parse time","Add an instanceof Number guard before subtracting: if (a instanceof Number && b instanceof Number) { return ((Number)a).doubleValue() - ((Number)b).doubleValue(); }","Fix the index mapping so the field is consistently typed as a numeric type (integer, float, etc.) across all documents","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"],"exampleFix":"// before\ndef a = doc['amount'].value;\ndef b = doc['offset'].value;\ndef result = a - b;\n\n// after\ndouble a = doc['amount'].value;\ndouble b = doc['offset'].value;\ndouble result = a - b;","handlingStrategy":"type-guard","validationCode":"// Before subtraction, verify both def values are numeric\ndef a = params['x'];\ndef b = params['y'];\nif (a instanceof Number && b instanceof Number) {\n  return ((Number) a).doubleValue() - ((Number) b).doubleValue();\n} else {\n  return 0.0; // or throw a descriptive error\n}","typeGuard":"// Check if a def value is safe for arithmetic\nboolean isNumeric(def v) {\n  return v instanceof Number;\n}","tryCatchPattern":null,"preventionTips":["Avoid def for variables involved in arithmetic; use explicit numeric types (int, long, float, double)","Verify field mappings with GET /index/_mapping before writing scripts that rely on numeric fields","Use doc['field'].value with a typed receiver to surface type mismatches at script compile time","Test scripts against documents with all expected field types before deploying to production"],"tags":["painless","elasticsearch","def","type-error","arithmetic","runtime","subtraction"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}