grpc/grpc-go · critical

Cannot free freed buffer

Error message

Cannot free freed buffer

What it means

buffer.Free() (mem/buffers.go:155) atomically decrements the refcount and panics at line 157-159 if the result is negative - i.e. Free() was called more times than there are references. This is a double-free guard: without it the same backing slice would be returned to the BufferPool twice and handed to two unrelated callers, corrupting data.

Source

Thrown at mem/buffers.go:158

}

func (b *buffer) ReadOnlyData() []byte {
	if b.rootBuf == nil {
		panic("Cannot read freed buffer")
	}
	return b.data
}

func (b *buffer) Ref() {
	if b.refs.Add(1) <= 1 {
		panic("Cannot ref freed buffer")
	}
}

func (b *buffer) Free() {
	refs := b.refs.Add(-1)
	if refs < 0 {
		panic("Cannot free freed buffer")
	}
	if refs > 0 {
		return
	}

	b.data = nil
	if b.rootBuf == b {
		// This buffer is the owner of the data slice and its ref count reached
		// 0, free the slice.
		if b.pool != nil {
			b.pool.Put(b.origData)
			b.pool = nil
		}
		b.origData = nil
	} else {
		// This buffer doesn't own the data slice, decrement a ref on the root
		// buffer.
		b.rootBuf.Free()

View on GitHub (pinned to 0c51461d27)

Solutions

  1. Each owner frees exactly the references it created/obtained via Ref(); never free a reference you did not take.
  2. Avoid mixing `defer b.Free()` with an in-function `b.Free()`; pick one owner of each reference.
  3. When handing a buffer to another goroutine, Ref() first and let the receiver own (and Free) that new reference.

Example fix

// before (double free)
func process(b mem.Buffer) {
    defer b.Free()
    if cond {
        b.Free() // second free -> panic
        return
    }
}

// after
func process(b mem.Buffer) {
    defer b.Free() // single owner
    if cond {
        return
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Give each reference a single, clear owner that frees it exactly once.
type ref struct {
    b    mem.Buffer
    done bool
}
func (r *ref) free() {
    if r.done {
        log.Print("double free avoided")
        return
    }
    r.done = true
    r.b.Free()
}

Prevention

When it happens

Trigger: Calling Free() twice on the same reference; two goroutines both Free()-ing a buffer that only one of them owned (forgot to Ref()); a cleanup path (defer) that frees plus an explicit Free() on the same reference.

Common situations: A defer b.Free() plus an explicit b.Free() in the same function; passing a buffer to a consumer that frees it while the producer also frees it; refcount bookkeeping bugs in custom codecs/interceptors.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/1bf7c46a1722ed03. Report an issue: GitHub.