temporalio/temporal · error
Cannot use Put API in Pin mode. Use Delete and…
Error message
Cannot use Put API in Pin mode. Use Delete and PutIfNotExist if necessary
What it means
common/cache/lru.go's Put panics when the cache is created in Pin mode. A pinned cache has a fixed working set (entries are effectively locked in), so unconditional Put would violate pinning semantics; the API instead directs users to Delete followed by PutIfNotExist. The panic enforces the restricted API surface for pinned caches.
Solutions
- On a pinned cache, replace Put with PutIfNotExist (and Delete first if replacement is truly required)
- Remove Pin mode if the workload genuinely needs unconditional overwrite semantics
- Route all writes through a wrapper that branches on the cache's pin mode
Example fix
// before
if old := cache.Put(key, val); old != nil { ... }
// after (pinned cache)
cache.Delete(key)
if cache.PutIfNotExist(key, val) { ... }
Defensive patterns
Strategy: validation
Validate before calling
if cacheIsPinned {
cache.Delete(key)
cache.PutIfNotExist(key, val)
} else {
cache.Put(key, val)
} Type guard
func canPut(c *lru) bool { return !c.pin } Prevention
- Branch write logic on the cache's pin mode
- Prefer PutIfNotExist for read-through caching patterns
- Document pinned caches at creation sites so callers know Put is unavailable
When it happens
Trigger: Creating a cache with the pin option enabled (Cache(Options{Pin: true}) or equivalent) and then calling c.Put(key, value) directly.
Common situations: Cache size set equal to expected working set with Pin to prevent eviction, then generic cache-writing code that uses Put; refactored code sharing a Put helper between pinned and non-pinned caches; migration from a non-pinned to a pinned cache without updating call sites.
Related errors
- LRU cache iterator Next called when there is no next item
- chasm visibilityTaskHandler should not be called directly
- internal error: call to RetryLockedSource.Seed
- Simple cache iterator Next called when there is no next item
- ActivityTaskScheduledEventAttributes.ActivityID is not set
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/8e4e2d30766d1a73.
Report an issue: GitHub.
Appendix: source
Thrown at common/cache/lru.go:220
entry := element.Value.(*entryImpl)
if c.isEntryExpired(entry, c.timeSource.Now().UTC()) {
// Entry has expired
c.deleteInternal(element)
return nil
}
metrics.CacheEntryAgeOnGet.With(c.metricsHandler).Record(c.timeSource.Now().UTC().Sub(entry.createTime))
c.updateEntryRefCount(entry)
c.byAccess.MoveToFront(element)
return entry.value
}
// Put puts a new value associated with a given key, returning the existing value (if present)
func (c *lru) Put(key any, value any) any {
if c.pin {
panic("Cannot use Put API in Pin mode. Use Delete and PutIfNotExist if necessary")
}
val, _ := c.putInternal(key, value, true)
return val
}
// PutIfNotExist puts a value associated with a given key if it does not exist
func (c *lru) PutIfNotExist(key any, value any) (any, error) {
existing, err := c.putInternal(key, value, false)
if err != nil {
return nil, err
}
if existing == nil {
// This is a new value
return value, err
}
return existing, errView on GitHub (pinned to bde624efd1)