elastic/elasticsearch · error · PainlessError

script allocation limit exceeded: allocation of [{}] bytes b

Error message

script allocation limit exceeded: allocation of [{}] bytes brings the running total to [{}] bytes, over the limit of [{}] bytes

What it means

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.

Source

Thrown at modules/lang-painless/src/main/java/org/elasticsearch/painless/AllocationGuard.java:70

    /**
     * Logs a {@code WARN} and throws a {@link PainlessError} describing an allocation that pushed a script over its limit.
     * {@link PainlessError} is an {@link Error}, so it cannot be caught from Painless source. Never returns normally. The
     * specific allocation that crossed the limit is not reported: it is whichever happened to tip the running total, not
     * necessarily the dominant cost, so naming it would mislead more than help.
     *
     * @param attemptedBytes the size of the allocation that tripped the limit
     * @param totalBytes the running total after charging the allocation
     * @param limitBytes the per-context limit
     */
    public static void allocationLimitExceeded(long attemptedBytes, long totalBytes, long limitBytes) {
        logger.warn(
            "Painless script allocation limit exceeded: allocation of [{}] bytes brings running total to [{}] bytes (limit [{}] bytes)",
            attemptedBytes,
            totalBytes,
            limitBytes
        );
        throw new PainlessError(
            "script allocation limit exceeded: allocation of ["
                + attemptedBytes
                + "] bytes brings the running total to ["
                + totalBytes
                + "] bytes, over the limit of ["
                + limitBytes
                + "] bytes"
        );
    }
}

View on GitHub (pinned to db6a809a66)

Solutions

  1. 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.
  2. 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.
  3. Move the heavy transformation out of Painless into an ingest pipeline processor or application code.
  4. Profile by enabling the guard at a low threshold and bisecting which allocation trips it.

Example fix

// before (script_fields, builds huge string)
"script": { "source": "String s = ''; for (int i = 0; i < 100000; i++) { s += i } return s" }
// after (bounded, or raise setting)
PUT _cluster/settings
{ "persistent": { "script.painless.max_allocation_bytes": "256mb" } }
// and/or rewrite to avoid O(n^2) concatenation
Defensive patterns

Strategy: validation

Validate before calling

// Estimate script working set before deploying; or check the setting is adequate
async function checkAllocationBudget(client, estimatedBytes) {
  const settings = await client.cluster.getSettings({ include_defaults: true });
  const max = (settings.persistent['script.painless.max_allocation_bytes']
            || settings.defaults['script.painless.max_allocation_bytes']);
  if (max && max !== '-1b' && parseBytes(max) < estimatedBytes) {
    throw new Error(`Estimated allocation ${estimatedBytes} exceeds ${max}; raise the setting or rewrite the script`);
  }
}
function parseBytes(s){ /* parse '64mb' -> number */ }

Try / catch

// ScriptException/PainlessError is not safely catchable in-script; catch at the API call boundary:
// try { await client.search({body}) } catch (e) { if (/allocation limit exceeded/.test(e.message)) { /* rewrite script or raise setting */ } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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