elastic/elasticsearch · error · ScriptException
compile error
Error message
compile error
What it means
This is the catch-all wrapper thrown by convertToScriptException whenever ANY underlying error occurs while compiling a Painless script (syntax, type, reference, etc.). The original throwable is wrapped via ErrorCauseWrapper.maybeWrap and rethrown as a ScriptException whose message is the literal string 'compile error'. The real diagnostic is in the cause and in the scriptStack list, which contains a snippet of the failing script region plus a '^---- HERE' pointer line computed from the WriterConstants.CLASS_NAME stack frame.
Source
Thrown at modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java:497
snippet.append(" ...");
}
scriptStack.add(snippet.toString());
StringBuilder pointer = new StringBuilder();
if (startOffset > 0) {
pointer.append(" ");
}
for (int i = startOffset; i < offset; i++) {
pointer.append(' ');
}
pointer.append("^---- HERE");
scriptStack.add(pointer.toString());
pos = new ScriptException.Position(originalOffset, startOffset, endOffset);
}
break;
}
}
Throwable cause = ErrorCauseWrapper.maybeWrap(t);
throw new ScriptException("compile error", cause, scriptStack, scriptSource, PainlessScriptEngine.NAME, pos);
}
// very simple heuristic: +/- 25 chars. can be improved later.
private static int getPreviousStatement(int offset) {
return Math.max(0, offset - 25);
}
private static int getNextStatement(String scriptSource, int offset) {
return Math.min(scriptSource.length(), offset + 25);
}
}
View on GitHub (pinned to db6a809a66)
Solutions
- Inspect the ScriptException.getScriptStack() and getCause() — the snippet and '^---- HERE' pointer pinpoint the failing offset in the script.
- Fix the syntax/type/reference error in the script text indicated by the cause.
- If the cause references an un-whitelisted method, replace it with an allowed Painless API or add an allowlist entry if you maintain a custom context.
- Re-run with the corrected script; the 'compile error' wrapper disappears once the underlying compile succeeds.
Example fix
// before (invalid: undeclared variable)
{"script": {"source": "doc['price'].value * faktor"}}
// after
{"script": {"source": "doc['price'].value * factor"}} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate script source before sending: a lightweight pre-check is not possible generically,
// but you can catch the wrapped exception and surface the cause.
try {
scriptService.compile(script, context);
} catch (ScriptException e) {
// real error is e.getCause() and e.getScriptStack()
} Try / catch
try {
CompiledScript compiled = scriptService.compile(script, ScriptContext.Standard.SEARCH);
} catch (ScriptException e) {
List<String> stack = e.getScriptStack(); // snippet + '^---- HERE' pointer
Throwable cause = e.getCause(); // underlying compile error
log.warn("Painless compile failed: {} | stack:{}", cause, stack);
throw e;
} Prevention
- Always read getScriptStack() and getCause() — the 'compile error' string itself carries no detail.
- Develop scripts in Kibana's Dev Console or the _execute API first to get fast feedback before deploying.
- Pin your ES version so whitelisted APIs do not silently change.
When it happens
Trigger: Compiling any Painless script that has a syntax error, references an undefined variable/function, performs a disallowed cast, or uses an un-whitelisted API. The trigger is compile() failing and control reaching the throw at line 497 of PainlessScriptEngine. Common entry points: _scripts/painless/_execute, inline scripts in _search, runtime fields, ingest pipelines, or any ScriptService.compile call.
Common situations: Typo in a Painless script field name; using a Java API that is not whitelisted for Painless; version upgrade that removed or renamed a previously allowed method; passing the wrong script context; dynamic/scripted_field with invalid syntax.
Related errors
- Painless can only implement interfaces that have a single me
- no execute method found
- convertFromDef must take a single Object as an argument, not
- [{}#ARGUMENTS] has length [2] but [{}#execute] takes [1] arg
- Painless needs a constant [String[] PARAMETERS] on all inter
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/eb4d90881a6d9f47.
Report an issue: GitHub.