temporalio/temporal · error
Can not invoke method on destroyed queue slice
Error message
Can not invoke method on destroyed queue slice
What it means
SliceImpl is a task-queue slice in the history service; once destroy() runs the slice is removed from the monitor and its trackers are nil, so any further method call is a use-after-free bug. stateSanityCheck panics to fail fast instead of operating on nil iterators/trackers. It guards methods like Scope, SplitByPredicate, CanMergeWithSlice, ShrinkScope, SelectTasks, MoreTasks, TaskStats, Clear.
Source
Thrown at service/history/queues/slice.go:447
s.iterators = []Iterator{
NewIterator(s.paginationFnProvider, s.scope.Range),
}
s.clear()
s.monitor.SetSlicePendingTaskCount(s, len(s.pendingExecutables))
}
func (s *SliceImpl) destroy() {
s.destroyed = true
s.iterators = nil
s.executableTracker = nil
s.monitor.RemoveSlice(s)
}
func (s *SliceImpl) stateSanityCheck() {
if s.destroyed {
panic("Can not invoke method on destroyed queue slice")
}
}
func (s *SliceImpl) newSlice(
scope Scope,
iterators []Iterator,
tracker *executableTracker,
) *SliceImpl {
slice := &SliceImpl{
paginationFnProvider: s.paginationFnProvider,
executableFactory: s.executableFactory,
scope: scope,
iterators: iterators,
executableTracker: tracker,
monitor: s.monitor,
maxPredicateSizeFn: s.maxPredicateSizeFn,
maxPendingKeysFn: s.maxPendingKeysFn,
metricsHandler: s.metricsHandler,View on GitHub (pinned to bde624efd1)
Solutions
- Never retain Slice references across queue operations; re-fetch slices from the queue manager after any split/merge
- Check slice ownership: only the owning QueueBase/tracker should call mutating methods
- Serialize access — queue slices are not goroutine-safe; use the queue's operation flow
- If reproducible, report with the stack trace to temporalio/temporal as an internal invariant violation
Example fix
// before: retaining a slice across a merge
slice := queue.GetSlices()[0]
queue.MergeSlices(...)
slice.ShrinkScope() // panics if slice was destroyed by merge
// after: re-acquire current slices from the queue after mutation
queue.MergeSlices(...)
for _, slice := range queue.GetSlices() { slice.ShrinkScope() } Defensive patterns
Strategy: type-guard
Type guard
func isUsable(s queues.Slice) bool {
type alive interface{ MoreTasks() bool }
defer func() { recover() }()
_ = s.MoreTasks() // panics if destroyed
return true
} Try / catch
// Guard retained references at goroutine boundaries:
func safeCall(s queues.Slice, fn func(queues.Slice)) (err error) {
defer func() { if r := recover(); r != nil { err = fmt.Errorf("slice destroyed: %v", r) } }()
fn(s)
return nil
} Prevention
- Do not retain *SliceImpl references across queue split/merge operations
- Re-fetch slices from the queue manager after any mutation
- Only let the owning queue tracker mutate slices
- Serialize all slice access through the queue's operation flow
When it happens
Trigger: Calling any public Slice method after the slice was destroyed — destroy() is invoked internally by appendMergedSlice when a merged slice's scope becomes empty, or by the queue tracker during re-bucketing; keeping a reference to a slice returned by a queue and calling methods on it after the queue merged/split slices.
Common situations: Holding a *SliceImpl across a queue operation that merges slices; concurrent access where one goroutine merges slices while another still uses an old reference; custom code extending the queue manager that retains slice pointers.
Related errors
- Can not append slice to existing list of slices, incoming sl
- Found overlapping incoming slices, left slice range: %v, rig
- reader with ID %v already exists
- Unable to split scope with range %v at %v
- Unable to merge scope with range %v with range %v by range
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/4763cd1d26ccd9e9.
Report an issue: GitHub.