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

  1. Ensure Close is called exactly once per Store — use sync.Once or remove duplicate close sites
  2. Track ownership so only the creator closes the store
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/b6206586232c3d07. Report an issue: GitHub.