temporalio/temporal · error
LRU cache iterator Next called when there is no next item
Error message
LRU cache iterator Next called when there is no next item
What it means
The LRU cache iterator in common/cache/lru.go panics when Next() is called after the iterator is exhausted (or before any call when there is no item). Callers must use HasNext() to check availability before Next(); iterating past the end is treated as a contract violation, not a returned error.
Source
Thrown at common/cache/lru.go:77
refCount int
size int
}
)
// Close closes the iterator
func (it *iteratorImpl) Close() {
it.lru.mut.Unlock()
}
// HasNext return true if there is more items to be returned
func (it *iteratorImpl) HasNext() bool {
return it.nextItem != nil
}
// Next return the next item
func (it *iteratorImpl) Next() Entry {
if it.nextItem == nil {
panic("LRU cache iterator Next called when there is no next item")
}
entry := it.nextItem.Value.(*entryImpl)
it.nextItem = it.nextItem.Next()
// make a copy of the entry so there will be no concurrent access to this entry
entry = &entryImpl{
key: entry.key,
value: entry.value,
size: entry.size,
createTime: entry.createTime,
}
it.prepareNext()
return entry
}
func (it *iteratorImpl) prepareNext() {
for it.nextItem != nil {
entry := it.nextItem.Value.(*entryImpl)View on GitHub (pinned to bde624efd1)
Solutions
- Guard every Next() call with it.HasNext()
- Use the standard `for it.HasNext() { e := it.Next() ... }` pattern
- Obtain a fresh iterator from the cache instead of reusing an exhausted one
- Synchronize access so only one goroutine consumes a given iterator
Example fix
// before
for {
entry := it.Next() // panics when exhausted
process(entry)
}
// after
for it.HasNext() {
process(it.Next())
}
Defensive patterns
Strategy: validation
Validate before calling
if !it.HasNext() { return ErrNoMoreEntries } Type guard
func safeNext(it cache.Iterator) (cache.Entry, bool) {
if !it.HasNext() {
return nil, false
}
return it.Next(), true
} Try / catch
func() (err error) {
defer func() {
if r := recover(); r != nil {
if strings.Contains(fmt.Sprint(r), "no next item") {
err = ErrIteratorExhausted
}
}
}()
return iterate(it)
} Prevention
- Always iterate with `for it.HasNext()`
- Never reuse an exhausted iterator; get a fresh one
- Avoid manual counted loops over cache iterators
- Only one goroutine should consume an iterator at a time
When it happens
Trigger: Calling it.Next() when it.HasNext() is false — typically a loop like `for { item := it.Next() ... }` without checking HasNext, or calling Next one extra time after a `for it.HasNext()` loop.
Common situations: Off-by-one in manual iteration over an LRU cache (e.g. draining a session/namespace cache); reusing an exhausted iterator for a second pass; concurrent iteration where another goroutine's consumption exhausts the iterator.
Related errors
- Cannot use Put API in Pin mode. Use Delete and PutIfNotExist
- Simple cache iterator Next called when there is no next item
- HistoryEventIterator Next() called without checking HasNext(
- HistoryEventIterator Next() should return either a history e
- Iterator encountered Next call when there is no next item
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/29757c958adefa4d.
Report an issue: GitHub.