grpc/grpc-go · critical
Cannot slice freed buffer
Error message
Cannot slice freed buffer
What it means
buffer.Slice(start, end) (mem/buffers.go:187) returns a new view into the buffer's data and panics at line 188-190 if b.rootBuf == nil (buffer already freed). Slicing freed memory would point at pooled/recycled bytes, so it is rejected. A successful Slice also takes a Ref() on the root (line 205) so the view keeps the data alive.
Source
Thrown at mem/buffers.go:189
}
b.origData = nil
} else {
// This buffer doesn't own the data slice, decrement a ref on the root
// buffer.
b.rootBuf.Free()
}
b.rootBuf = nil
bufferObjectPool.Put(b)
}
func (b *buffer) Len() int {
return len(b.ReadOnlyData())
}
func (b *buffer) Slice(start, end int) Buffer {
if b.rootBuf == nil {
panic("Cannot slice freed buffer")
}
data := b.data[start:end] // access the data to check slice bounds
if len(data) == 0 {
return emptyBuffer{}
}
if len(data) == len(b.data) {
b.Ref()
return b
}
// We are creating a new reference (view) to a portion of the root buffer's
// data. Therefore, we must increment the reference count of the root buffer
// to ensure the underlying data is not freed while this view is still in
// use.
b.rootBuf.Ref()
s := newBuffer()
s.data = dataView on GitHub (pinned to 0c51461d27)
Solutions
- Slice the buffer only while it is alive (before Free, or on a Ref()'d copy).
- If you need the sliced view beyond the current scope, take a Ref() first and Free() when done.
- Treat the input BufferSlice in CodecV2 as freed once Unmarshal returns; copy or Ref() anything you retain.
Example fix
// before b.Free() sub := b.Slice(0, 4) // panic: freed // after sub := b.Slice(0, 4) // slice while alive (Ref taken on root) b.Free() // free original ref; sub keeps data alive // later: sub.Free()
Defensive patterns
Strategy: validation
Validate before calling
// Slice only while alive; take a Ref first if lifetime is uncertain.
func safeSlice(b mem.Buffer, start, end int) mem.Buffer {
if b.Len() == 0 || start < 0 || end > b.Len() || start > end {
return nil
}
return b.Slice(start, end)
} Prevention
- Slice before Free, or on a Ref()'d copy.
- Remember Slice takes its own Ref on the root; free the view when done.
- Treat input buffers in CodecV2 as freed once Unmarshal returns.
When it happens
Trigger: Calling buf.Slice(a, b) after buf.Free(); slicing a buffer whose data was freed by another goroutine; slicing within a CodecV2 after the input BufferSlice was freed.
Common situations: Custom CodecV2/interceptor that slices input buffers after they were freed by the framework; concurrent stream processing where one path freed the buffer; reusing a buffer variable across iterations after freeing it.
Related errors
- Cannot read freed buffer
- Cannot ref freed buffer
- Cannot split freed buffer
- Cannot free 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/67520ddf4f8b226c.
Report an issue: GitHub.