temporalio/temporal · error

Unable to merge queue slice of type %T with type %T

Error message

Unable to merge queue slice of type %T with type %T

What it means

SliceImpl.MergeWithSlice also panics if the incoming slice cannot be type-asserted to *SliceImpl. The merge implementation needs concrete SliceImpl internals (scope, iterators, executable tracker); any other implementation of the Slice interface cannot be merged. This guards against mixing slice implementations.

Source

Thrown at service/history/queues/slice.go:176

func (s *SliceImpl) CanMergeWithSlice(slice Slice) bool {
	s.stateSanityCheck()

	return s != slice && s.scope.Range.CanMerge(slice.Scope().Range)
}

func (s *SliceImpl) MergeWithSlice(slice Slice) []Slice {
	if s.scope.Range.InclusiveMin.CompareTo(slice.Scope().Range.InclusiveMin) > 0 {
		return slice.MergeWithSlice(s)
	}

	if !s.CanMergeWithSlice(slice) {
		panic(fmt.Sprintf("Unable to merge queue slice having scope %v with slice having scope %v", s.scope, slice.Scope()))
	}

	incomingSlice, ok := slice.(*SliceImpl)
	if !ok {
		panic(fmt.Sprintf("Unable to merge queue slice of type %T with type %T", s, slice))
	}

	if s.scope.CanMergeByRange(incomingSlice.scope) {
		return []Slice{s.mergeByRange(incomingSlice)}
	}

	mergedSlices := make([]Slice, 0, 3)
	currentLeftSlice, currentRightSlice := s.splitByRange(incomingSlice.Scope().Range.InclusiveMin)
	mergedSlices = appendMergedSlice(mergedSlices, currentLeftSlice)

	if currentRightMax := currentRightSlice.Scope().Range.ExclusiveMax; incomingSlice.CanSplitByRange(currentRightMax) {
		leftIncomingSlice, rightIncomingSlice := incomingSlice.splitByRange(currentRightMax)
		mergedMidSlice := currentRightSlice.mergeByPredicate(leftIncomingSlice)
		mergedSlices = appendMergedSlice(mergedSlices, mergedMidSlice)
		mergedSlices = appendMergedSlice(mergedSlices, rightIncomingSlice)
	} else {
		currentMidSlice, currentRightSlice := currentRightSlice.splitByRange(incomingSlice.Scope().Range.ExclusiveMax)
		mergedMidSlice := currentMidSlice.mergeByPredicate(incomingSlice)

View on GitHub (pinned to bde624efd1)

Solutions

  1. Pass only concrete *SliceImpl values created via NewSlice/NewSliceImpl
  2. Unwrap any decorator/wrapper to the underlying *SliceImpl before merging
  3. In tests, use the real slice constructor instead of mock Slice implementations

Example fix

// before
slice.MergeWithSlice(mockSlice) // panics: not *SliceImpl

// after
impl, ok := wrapped.(*queues.SliceImpl)
if !ok {
  return nil // or unwrap the decorator
}
result := slice.MergeWithSlice(impl)
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := other.(*queues.SliceImpl); !ok {
  return nil // or unwrap; cannot merge non-SliceImpl
}

Type guard

func asSliceImpl(s queues.Slice) (*queues.SliceImpl, bool) {
  impl, ok := s.(*queues.SliceImpl)
  return impl, ok
}

Try / catch

func safeMerge(s *queues.SliceImpl, other queues.Slice) (result []queues.Slice) {
  defer func() {
    if r := recover(); r != nil {
      result = nil
    }
  }()
  return s.MergeWithSlice(other)
}

Prevention

When it happens

Trigger: Passing a custom or wrapper type implementing the Slice interface (not *SliceImpl) to MergeWithSlice; passing a nil Slice with a non-nil interface value; wrapping slices in decorators for instrumentation.

Common situations: Test code supplying mock Slice implementations; production wrappers adding metrics/logging around slices; DI swaps introducing an alternate slice type.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/e6e123d8aa9d83d5. Report an issue: GitHub.