{"record":{"id":"ea35d61a3ebcb154","repo":"elastic/elasticsearch","slug":"script-allocation-limit-exceeded-allocation-of","errorCode":null,"errorMessage":"script allocation limit exceeded: allocation of [{}] bytes brings the running total to [{}] bytes, over the limit of [{}] bytes","messagePattern":"script allocation limit exceeded: allocation of \\[(.+?)\\] bytes brings the running total to \\[(.+?)\\] bytes, over the limit of \\[(.+?)\\] bytes","errorType":"error_code","errorClass":"PainlessError","httpStatus":null,"severity":"error","filePath":"modules/lang-painless/src/main/java/org/elasticsearch/painless/AllocationGuard.java","lineNumber":70,"sourceCode":"\n    /**\n     * Logs a {@code WARN} and throws a {@link PainlessError} describing an allocation that pushed a script over its limit.\n     * {@link PainlessError} is an {@link Error}, so it cannot be caught from Painless source. Never returns normally. The\n     * specific allocation that crossed the limit is not reported: it is whichever happened to tip the running total, not\n     * necessarily the dominant cost, so naming it would mislead more than help.\n     *\n     * @param attemptedBytes the size of the allocation that tripped the limit\n     * @param totalBytes the running total after charging the allocation\n     * @param limitBytes the per-context limit\n     */\n    public static void allocationLimitExceeded(long attemptedBytes, long totalBytes, long limitBytes) {\n        logger.warn(\n            \"Painless script allocation limit exceeded: allocation of [{}] bytes brings running total to [{}] bytes (limit [{}] bytes)\",\n            attemptedBytes,\n            totalBytes,\n            limitBytes\n        );\n        throw new PainlessError(\n            \"script allocation limit exceeded: allocation of [\"\n                + attemptedBytes\n                + \"] bytes brings the running total to [\"\n                + totalBytes\n                + \"] bytes, over the limit of [\"\n                + limitBytes\n                + \"] bytes\"\n        );\n    }\n}\n","sourceCodeStart":52,"sourceCodeEnd":81,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/lang-painless/src/main/java/org/elasticsearch/painless/AllocationGuard.java#L52-L81","documentation":"AllocationGuard.allocationLimitExceeded is the hard stop for the Painless per-context memory budget. Each significant allocation inside a running script is charged against a byte limit (script.painless.max_allocation_bytes, default disabled at -1b but commonly capped). When an allocation pushes the running total past the limit, the guard logs a warning and throws a PainlessError — a Throwable that is not normally catchable in the script, aborting execution.","triggerScenarios":"A Painless script (runtime, ingest, or script_fields) that allocates large buffers: building big strings via concatenation in a loop, List.of/Map.of with many entries, copying large doc values, or accumulating results. The per-context budget is exceeded mid-execution. Reproducible by raising script.painless.max_allocation_bytes and seeing the script succeed, lowering it and seeing it fail.","commonSituations":"Aggregations or runtime fields that materialize large collections. Ingest pipelines with grok/csv on huge documents. Scripts that join strings across many docs. Lowering the allocation setting for safety on shared clusters. Version upgrades that changed default allocation charging.","solutions":["Raise script.painless.max_allocation_bytes (cluster setting) within the allowed range [1b, 1gb] to fit the script's working set, or set -1b to disable.","Rewrite the script to stream/accumulate less: use StringBuilder with sized capacity, avoid intermediate copies, prefer doc-values direct reads, and narrow the query to reduce processed docs.","Move the heavy transformation out of Painless into an ingest pipeline processor or application code.","Profile by enabling the guard at a low threshold and bisecting which allocation trips it."],"exampleFix":"// before (script_fields, builds huge string)\n\"script\": { \"source\": \"String s = ''; for (int i = 0; i < 100000; i++) { s += i } return s\" }\n// after (bounded, or raise setting)\nPUT _cluster/settings\n{ \"persistent\": { \"script.painless.max_allocation_bytes\": \"256mb\" } }\n// and/or rewrite to avoid O(n^2) concatenation","handlingStrategy":"validation","validationCode":"// Estimate script working set before deploying; or check the setting is adequate\nasync function checkAllocationBudget(client, estimatedBytes) {\n  const settings = await client.cluster.getSettings({ include_defaults: true });\n  const max = (settings.persistent['script.painless.max_allocation_bytes']\n            || settings.defaults['script.painless.max_allocation_bytes']);\n  if (max && max !== '-1b' && parseBytes(max) < estimatedBytes) {\n    throw new Error(`Estimated allocation ${estimatedBytes} exceeds ${max}; raise the setting or rewrite the script`);\n  }\n}\nfunction parseBytes(s){ /* parse '64mb' -> number */ }","typeGuard":null,"tryCatchPattern":"// ScriptException/PainlessError is not safely catchable in-script; catch at the API call boundary:\n// try { await client.search({body}) } catch (e) { if (/allocation limit exceeded/.test(e.message)) { /* rewrite script or raise setting */ } }","preventionTips":["Profile scripts on a representative dataset before production.","Set a conservative max_allocation_bytes on shared clusters and monitor warnings.","Avoid O(n^2) string concatenation; use StringBuilder or pre-sized collections.","Narrow queries to reduce docs processed per script execution."],"tags":["painless","script","memory-limit","allocation","runtime"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}