vitessio/vitess · error
theine.Store: double close
Error message
theine.Store: double close
What it means
theine.Store.Close uses an atomic open flag; if Close is called when open is already false (i.e. after a previous Close), it panics with 'double close'. The library treats closing twice as a caller bug rather than an idempotent operation.
Source
Thrown at go/cache/theine/store.go:593
func (s *Store[K, V]) Range(epoch uint32, f func(key K, value V) bool) {
for _, shard := range s.shards {
shard.mu.RLock()
for _, entry := range shard.hashmap {
if entry.epoch.Load() < epoch {
continue
}
if !f(entry.key, entry.value) {
shard.mu.RUnlock()
return
}
}
shard.mu.RUnlock()
}
}
func (s *Store[K, V]) Close() {
if !s.open.Swap(false) {
panic("theine.Store: double close")
}
for _, s := range s.shards {
s.mu.Lock()
clear(s.hashmap)
s.mu.Unlock()
}
close(s.writebuf)
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Ensure Close is called exactly once per Store — use sync.Once or remove duplicate close sites
- Track ownership so only the creator closes the store
- If needed, wrap the store with a once-guard: var once sync.Once; once.Do(func(){ s.Close() })
Example fix
// before
s.Close()
...
s.Close() // panics: double close
// after
var closeOnce sync.Once
closeOnce.Do(func() { s.Close() }) Defensive patterns
Strategy: try-catch
Validate before calling
func (c *CacheWrapper[K, V]) CloseOnce() {
c.closeOnce.Do(func() { c.store.Close() })
} Try / catch
func safeClose[K, V any](s *cache.Store[K, V]) (closed bool) {
defer func() {
if r := recover(); r != nil {
if fmt.Sprint(r) == "theine.Store: double close" {
closed = true // already closed, treat as success
} else {
panic(r)
}
}
}()
s.Close()
return true
} Prevention
- Guard Close with sync.Once at every call site
- Define a single owner responsible for store shutdown
- Remove redundant defer+explicit Close pairs
When it happens
Trigger: Calling s.Close() twice on the same theine.Store instance, often from two code paths each believing they own shutdown (e.g. defer Close plus explicit Close).
Common situations: Double shutdown during app teardown; defer + explicit close; cache wrapper layer also closing the underlying store; tests closing in both t.Cleanup and the test body.
Related errors
- cannot Notify after starting to watch a config
- BUG: thread with ID: %v must not access closed Throttler
- BUG: thread with ID: %v already finished
- executor is closed
- ErrStreamClosed
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/b6206586232c3d07.
Report an issue: GitHub.