cloudflare/pingora · error
take_write_lock() called without cache lock
Error message
take_write_lock() called without cache lock
What it means
pingora-cache's HttpCache::take_write_lock() transfers the cache write lock from this request to another (cache-lock leader handoff). The first .expect() (lib.rs:1564) fires when self has no lock_ctx at all: cache locking was never enabled/acquired for this request, so there is nothing to take, and the process panics with 'take_write_lock() called without cache lock'. The method's doc explicitly requires calling is_cache_lock_writer() first.
Source
Thrown at pingora-cache/src/lib.rs:1564
}
/// Maximum number of cache lock retries configured for this request.
pub fn cache_lock_max_retries(&self) -> Option<usize> {
self.inner_enabled()
.lock_ctx
.as_ref()
.and_then(|l| l.max_retries)
}
/// Take the write lock from this request to transfer it to another one.
/// # Panic
/// Call is_cache_lock_writer() to check first, will panic otherwise.
pub fn take_write_lock(&mut self) -> (WritePermit, &'static CacheKeyLockImpl) {
let lock_ctx = self
.inner_enabled_mut()
.lock_ctx
.as_mut()
.expect("take_write_lock() called without cache lock");
let lock = lock_ctx
.lock
.take()
.expect("take_write_lock() called without lock");
match lock {
Locked::Write(w) => (w, lock_ctx.cache_lock),
Locked::Read(_) => panic!("take_write_lock() called on read lock"),
}
}
/// Set the write lock, which is usually transferred from [Self::take_write_lock()]
///
/// # Panic
/// Panics if cache lock was not originally configured for this request.
// TODO: it may make sense to allow configuring the CacheKeyLock here too that the write permit
// is associated with
// (The WritePermit comes from the CacheKeyLock and should be used when releasing from the CacheKeyLock,
// shouldn't be possible to give a WritePermit to a request using a different CacheKeyLock)View on GitHub (pinned to 0046038bd4)
Solutions
- Gate every call exactly as the docs say: if session.cache.is_cache_lock_writer() { let (permit, lock) = session.cache.take_write_lock(); ... }
- Ensure the cache backend actually has a cache lock configured (CacheLock/lock settings) so misses acquire a lock_ctx
- Only take the lock from code paths that are guaranteed to run for the cache-miss leader request
Example fix
// before
let (permit, lock) = session.cache.take_write_lock(); // panics without a lock ctx
// after
if session.cache.is_cache_lock_writer() {
let (permit, lock) = session.cache.take_write_lock();
// hand off via set_write_lock() on the filler request
} Defensive patterns
Strategy: validation
Validate before calling
// Required guard, straight from the method's doc comment (lib.rs:1556-1558)
if session.cache.is_cache_lock_writer() {
let (permit, lock) = session.cache.take_write_lock();
// ...transfer the permit to the filler request...
} Prevention
- Never call take_write_lock() without the is_cache_lock_writer() check
- Keep all lock-transfer logic in one helper so every path is guarded identically
- Test cache hits and non-cacheable requests through the same filter to prove they skip the transfer
When it happens
Trigger: Calling session.cache.take_write_lock() on a request that never went through the cache-lock acquisition path: cache locking not configured on the cache backend, the request arrived as a cache hit / bypass, or the lock context was already dropped by disable().
Common situations: Custom lock-transfer code (streaming miss, background revalidation fill) invoked from filters that also run on cache hits or non-cacheable requests; enabling cache lock only on some keys; upgrading code written against APIs where lock_ctx was unconditional.
Related errors
- take_write_lock() called without lock
- must have read body buf
- body buf exists once a partial chunk head was buffered
- body buf exists once a chunk was parsed out of it
- cache_key_callback must be implemented when caching is enabl
AI-assisted analysis of cloudflare/pingora@0046038bd4 (2026-08-16).
Data as JSON: /api/errors/a48a26c68583706b.
Report an issue: GitHub.