grpc/grpc-go · critical
Cannot ref freed buffer
Error message
Cannot ref freed buffer
What it means
buffer.Ref() (mem/buffers.go:149) increments the atomic refcount and panics at line 150-152 if the new count is <= 1, meaning the prior count was <= 0 (already freed). You cannot take a new reference to a buffer whose lifetime has ended; doing so would resurrect pooled memory. Each goroutine that wants to use a buffer must Ref() it while it is still alive.
Source
Thrown at mem/buffers.go:151
copy(buf, data)
return buf
}
buf := pool.Get(len(data))
copy(*buf, data)
return NewBuffer(buf, pool)
}
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)View on GitHub (pinned to 0c51461d27)
Solutions
- Ref() the buffer at the point you receive it (while it is guaranteed alive), before storing or sending it.
- Pair every Ref() with a later Free() in the same scope that owns the reference.
- Never assume a buffer received from an API is still alive after that API returns; take your own reference up front.
Example fix
// before
ch <- buf // send raw buffer
go func() {
b := <-ch
b.Ref() // panic: already freed by sender
}()
buf.Free()
// after
buf.Ref() // take ref while alive
ch <- buf
go func() {
b := <-ch
defer b.Free()
_ = b.ReadOnlyData()
}()
buf.Free() Defensive patterns
Strategy: validation
Validate before calling
// Ref() at the point of receipt, while the buffer is guaranteed alive.
func handOff(b mem.Buffer) mem.Buffer {
b.Ref() // take a new reference before giving it away
return b
} Prevention
- Ref() buffers while they are alive, before storing or sending them.
- Pair each Ref() with exactly one Free() in the owning scope.
- Never assume a buffer is alive after the API that produced it returns.
When it happens
Trigger: Calling buf.Ref() after buf.Free() has already dropped the count to 0; ref-ing a buffer obtained from a path that already freed it; concurrent code where one goroutine freed the buffer before another called Ref().
Common situations: Sharing a Buffer across goroutines without Ref()-before-send; stashing a buffer for later use and the original owner already freed it; off-by-one in manual refcount management in a custom codec or interceptor.
Related errors
- Cannot read freed buffer
- Cannot free freed buffer
- Cannot slice freed buffer
- Cannot split freed buffer
- slice bounds out of range [%d:%d] with length 0
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/6049d2c6bffeffd5.
Report an issue: GitHub.