temporalio/temporal · error

HistoryEventIterator Next() called without checking HasNext(

Error message

HistoryEventIterator Next() called without checking HasNext()

What it means

PagingIteratorImpl.Next() panics if HasNext() is false at call time. The iterator contract requires callers to check HasNext() before every Next(); calling Next() after exhaustion (or before any page is loaded) is a programming error, signaled by this panic (the message text mentions HistoryEventIterator, the original use of this generic iterator).

Source

Thrown at common/collection/paging_iterator.go:71

	}

	// still have local cached item to return
	if iter.nextPageItemIndex < len(iter.pageItems) {
		return true
	}

	if len(iter.pageToken) != 0 {
		iter.getNextPage()
		return iter.HasNext()
	}

	return false
}

// Next return next item or err
func (iter *PagingIteratorImpl[V]) Next() (V, error) {
	if !iter.HasNext() {
		panic("HistoryEventIterator Next() called without checking HasNext()")
	}

	if iter.pageErr != nil {
		err := iter.pageErr
		iter.pageErr = nil
		var v V
		return v, err
	}

	// we have cached events
	if iter.nextPageItemIndex < len(iter.pageItems) {
		index := iter.nextPageItemIndex
		iter.nextPageItemIndex++
		return iter.pageItems[index], nil
	}

	panic("HistoryEventIterator Next() should return either a history event or a err")
}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Use the documented loop shape: for iter.HasNext() { item, err := iter.Next(); ... }
  2. Return on the first non-nil error from Next(); after an error HasNext() becomes false
  3. If integrating third-party loops, wrap Next() with a helper that recovers panics and converts them to errors

Example fix

// before
for {
  item, err := iter.Next()
  if err != nil { break }
  process(item)
}
// after
for iter.HasNext() {
  item, err := iter.Next()
  if err != nil { return err }
  process(item)
}
Defensive patterns

Strategy: validation

Validate before calling

if !iter.HasNext() {
  return // or break out of the loop; never call Next() here
}

Try / catch

func safeNext[V any](iter *collection.PagingIteratorImpl[V]) (v V, err error) {
  defer func() { if r := recover(); r != nil { err = fmt.Errorf("Next() misuse: %v", r) } }()
  return iter.Next()
}

Prevention

When it happens

Trigger: for { item, err := it.Next(); ... } without an it.HasNext() guard; continuing to call Next() after HasNext() returned false due to a terminal page error or exhausted token.

Common situations: Pagination loops over history events / visibility records that assume Next() returns a terminal error instead of requiring HasNext(); refactors that drop the HasNext() check.

Related errors


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