prestodb/presto · error · PrestoException

GENERIC_INTERNAL_ERROR

GENERIC_INTERNAL_ERROR

Error message

Operator %s has non-zero system memory (%d bytes) after destroy()

What it means

OperatorContext.destroy closes the operator's memory context then asserts the operator released all system memory. A non-zero remaining system memory reservation after destroy() is an internal invariant violation (a leak in the operator implementation), thrown as GENERIC_INTERNAL_ERROR.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/operator/OperatorContext.java:406

    public void destroy()
    {
        // reset memory revocation listener so that OperatorContext doesn't hold any references to Driver instance
        synchronized (this) {
            memoryRevocationRequestListener = null;
        }

        // memoize the result of and then clear any reference to the original suppliers (which might otherwise retain operators or other large objects)
        Supplier<? extends OperatorInfo> infoSupplier = this.infoSupplier.get();
        if (infoSupplier != null) {
            OperatorInfo info = infoSupplier.get();
            this.infoSupplier.set(info == null ? null : Suppliers.ofInstance(info));
        }

        operatorMemoryContext.close();

        if (operatorMemoryContext.getSystemMemory() != 0) {
            throw new PrestoException(GENERIC_INTERNAL_ERROR, format("Operator %s has non-zero system memory (%d bytes) after destroy()", this, operatorMemoryContext.getSystemMemory()));
        }

        if (operatorMemoryContext.getUserMemory() != 0) {
            throw new PrestoException(GENERIC_INTERNAL_ERROR, format("Operator %s has non-zero user memory (%d bytes) after destroy()", this, operatorMemoryContext.getUserMemory()));
        }

        if (operatorMemoryContext.getRevocableMemory() != 0) {
            throw new PrestoException(GENERIC_INTERNAL_ERROR, format("Operator %s has non-zero revocable memory (%d bytes) after destroy()", this, operatorMemoryContext.getRevocableMemory()));
        }
    }

    public SpillContext getSpillContext()
    {
        return spillContext;
    }

    public void moreMemoryAvailable()
    {

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Fix the operator to release/reserve-free all system memory before or during close/destroy
  2. Update Presto — this class of leak is often fixed in later releases
  3. Capture the operator name from the message and report with the plan fragment to isolate the leaking operator

Example fix

// before
close();
// after (operator must release before context close)
localSystemMemoryContext.close(); // frees tracked allocations
buffer = null;
Defensive patterns

Strategy: try-catch

Validate before calling

// operator authors: assert released before close
if (systemMemoryContext.getBytes() != 0) { systemMemoryContext.setBytes(0); }

Try / catch

// engine-side; users should catch and escalate as a bug report
try { executeQuery(sql); } catch (PrestoException e) {
    if (e.getMessage() != null && e.getMessage().contains("non-zero system memory")) {
        log.error("Operator memory leak after destroy", e); // file bug with operator name
    }
}

Prevention

When it happens

Trigger: destroy() is called during operator teardown while operatorMemoryContext.getSystemMemory() != 0, i.e. an operator allocated system memory (e.g. buffers, native allocations) and never freed/reserved-released it.

Common situations: Custom operator/connector bugs leaking memory contexts; failed operators that skip cleanup paths; pagination/buffer not set to null before close.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/f584f731173b3bc2. Report an issue: GitHub.