apache/kafka · error · IllegalArgumentException

requested size {sizeBytes}<=0

Error message

requested size {sizeBytes}<=0

What it means

Thrown by SimpleMemoryPool.tryAllocate(int sizeBytes) when the requested allocation size is less than 1. The pool refuses zero- or negative-length buffers because they would corrupt the available-memory accounting (the AtomicLong is decremented by sizeBytes on success). It is an IllegalArgumentException raised before any CAS on the pool counter.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/memory/SimpleMemoryPool.java:57

    protected final int maxSingleAllocationSize;
    protected final AtomicLong startOfNoMemPeriod = new AtomicLong(); //nanoseconds
    protected volatile Sensor oomTimeSensor;

    public SimpleMemoryPool(long sizeInBytes, int maxSingleAllocationBytes, boolean strict, Sensor oomPeriodSensor) {
        if (sizeInBytes <= 0 || maxSingleAllocationBytes <= 0 || maxSingleAllocationBytes > sizeInBytes)
            throw new IllegalArgumentException("must provide a positive size and max single allocation size smaller than size."
                + "provided " + sizeInBytes + " and " + maxSingleAllocationBytes + " respectively");
        this.sizeBytes = sizeInBytes;
        this.strict = strict;
        this.availableMemory = new AtomicLong(sizeInBytes);
        this.maxSingleAllocationSize = maxSingleAllocationBytes;
        this.oomTimeSensor = oomPeriodSensor;
    }

    @Override
    public ByteBuffer tryAllocate(int sizeBytes) {
        if (sizeBytes < 1)
            throw new IllegalArgumentException("requested size " + sizeBytes + "<=0");
        if (sizeBytes > maxSingleAllocationSize)
            throw new IllegalArgumentException("requested size " + sizeBytes + " is larger than maxSingleAllocationSize " + maxSingleAllocationSize);

        long available;
        boolean success = false;
        //in strict mode we will only allocate memory if we have at least the size required.
        //in non-strict mode we will allocate memory if we have _any_ memory available (so available memory
        //can dip into the negative and max allocated memory would be sizeBytes + maxSingleAllocationSize)
        long threshold = strict ? sizeBytes : 1;
        while ((available = availableMemory.get()) >= threshold) {
            success = availableMemory.compareAndSet(available, available - sizeBytes);
            if (success)
                break;
        }

        if (success) {
            maybeRecordEndOfDrySpell();
        } else {

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Inspect the sizeBytes value in the message — if it is 0 or negative, find the caller that computed it and fix the sizing logic.
  2. Guard the call site: if a payload can legitimately be empty, short-circuit before tryAllocate rather than allocating a zero-length buffer.
  3. Audit the size computation (e.g. Records.LOG_OVERHEAD, record-batch header, compression overhead) for underflow when individual components are zero.
  4. Reproduce with producer logging at TRACE to capture the request that produced the bad size; look for record/compression-size calculation around the call.
  5. If you maintain a custom MemoryPool, ensure tryAllocate callers never pass sizes derived from unchecked user input.

Example fix

// before — computed size can go to zero/negative
int size = payload.length - HEADER_SIZE;
ByteBuffer buf = pool.tryAllocate(size);   // throws if payload smaller than HEADER_SIZE

// after — validate before allocating
int size = Math.max(payload.length - HEADER_SIZE, 1);
if (payload.length < HEADER_SIZE) {
    throw new IllegalArgumentException("payload " + payload.length + " smaller than header " + HEADER_SIZE);
}
ByteBuffer buf = pool.tryAllocate(size);
Defensive patterns

Strategy: validation

Validate before calling

// Guard tryAllocate against non-positive sizes before calling.
int sizeBytes = ...;
if (sizeBytes < 1) {
    throw new IllegalArgumentException("Cannot allocate " + sizeBytes + " bytes; size must be >= 1");
}
ByteBuffer buf = pool.tryAllocate(sizeBytes);

Type guard

// Treat only positive ints as valid allocation requests.
static boolean isAllocatableSize(int sizeBytes) {
    return sizeBytes > 0;
}

Try / catch

try {
    buf = pool.tryAllocate(sizeBytes);
} catch (IllegalArgumentException e) {
    // caller passed garbage — drop the request, do not retry with the same size.
    log.warn("Rejected allocation of {} bytes", sizeBytes);
    buf = null;
}

Prevention

When it happens

Trigger: Calling tryAllocate(n) where n <= 0 — most often reached transitively when a record batch, send buffer, or request size is computed as zero or negative and handed to the memory pool. In the producer path this can occur when a Serializers/CompressionType produces an empty payload or when a caller computes a size as (a - b) that goes negative under load.

Common situations: A record with an empty value and an empty key combined with a compressor that writes a zero-byte payload; an arithmetic underflow producing a negative size (e.g. size = total - overhead where overhead > total); test code that pre-sizes a buffer from an uninitialized int default of 0; a custom protocol layer that calls tryAllocate(0) as a 'no-op' placeholder; an off-by-one when subtracting record-batch overhead.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/9131db60b747d74a.json. Report an issue: GitHub.