temporalio/temporal · error

Simple cache iterator Next called when there is no next item

Error message

Simple cache iterator Next called when there is no next item

What it means

The simple cache (common/cache/simple.go) iterator panics when Next() is called with no remaining item — the same contract as the LRU iterator. HasNext() must return true before Next() is legal. There is no graceful end-of-iteration return; the framework treats over-iteration as a caller bug.

Source

Thrown at common/cache/simple.go:46

		key   any
		value any
	}
)

// Close closes the iterator
func (it *simpleItr) Close() {
	it.simple.RUnlock()
}

// HasNext return true if there is more items to be returned
func (it *simpleItr) HasNext() bool {
	return it.nextItem != nil
}

// Next returns the next item
func (it *simpleItr) Next() Entry {
	if it.nextItem == nil {
		panic("Simple cache iterator Next called when there is no next item")
	}

	// nolint:revive
	entry := it.nextItem.Value.(*simpleEntry)
	it.nextItem = it.nextItem.Next()
	// make a copy of the entry so there will be no concurrent access to this entry
	entry = &simpleEntry{
		key:   entry.key,
		value: entry.value,
	}
	return entry
}

func (e *simpleEntry) Key() any {
	return e.key
}

func (e *simpleEntry) Value() any {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Check it.HasNext() before every Next() call
  2. Iterate with `for it.HasNext() { e := it.Next() ... }`
  3. Create a new iterator for each traversal instead of reusing exhausted ones

Example fix

// before
for i := 0; i < expectedCount; i++ {
    e := it.Next() // panics if cache has fewer items
}
// after
for it.HasNext() {
    e := it.Next()
    process(e)
}
Defensive patterns

Strategy: validation

Validate before calling

for it.HasNext() {
    e := it.Next()
    process(e)
}

Type guard

func nextOrNone(it *simpleItr) (cache.Entry, bool) {
    if !it.HasNext() {
        return nil, false
    }
    return it.Next(), true
}

Prevention

When it happens

Trigger: Calling Next() on a simpleItr after HasNext() returned false, or calling Next() before checking at all on an empty cache snapshot; calling Next() more times than the number of entries.

Common situations: Dumping/migrating all entries from a simple cache (e.g. namespace or shard caches) with a manual counter that overruns; iterating a cache that changed size between counting and iterating; copying LRU-style loop code into simple-cache code with an off-by-one.

Related errors


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