laravel/framework · warning · RuntimeException

This lock driver does not support refreshing locks.

Error message

This lock driver does not support refreshing locks.

What it means

The base Lock::refresh is a stub that always throws. Subclasses with refresh support (e.g., RedisLock, DatabaseLock) override this method to extend the TTL atomically. If you call refresh() on a lock instance whose driver does not override it, you hit the base implementation. This signals that the underlying store cannot safely extend a lock without re-acquiring it.

Solutions

  1. Switch to a driver whose Lock subclass overrides refresh(): RedisLock or DatabaseLock.
  2. Re-acquire instead of refresh: release() the current lock and acquire() a new one with the desired TTL.
  3. If authoring a custom Lock, override refresh($seconds = null) to atomically extend the TTL (e.g., via EXPIRE with owner token check).

Example fix

// before
$lock = Cache::lock('process-' . $id, 30);
// long-running work on array driver
$lock->refresh(30); // RuntimeException

// after - re-acquire
$lock->release();
$lock = Cache::block('process-' . $id, 30, function () {
    // continue work
});
// OR use a driver that supports refresh:
// CACHE_LOCK_STORE=redis
Defensive patterns

Strategy: validation

Validate before calling

// Validate the lock supports refresh before calling
$lock = Cache::lock('job-' . $id, 30);
$reflector = new \ReflectionMethod($lock, 'refresh');
if ($reflector->getDeclaringClass()->getName() === \Illuminate\Cache\Lock::class) {
    // base stub would throw; re-acquire instead
    $lock->release();
    $lock = Cache::lock('job-' . $id, 30);
} else {
    $lock->refresh(30);
}

Type guard

function lockSupportsRefresh(\Illuminate\Contracts\Cache\Lock $lock): bool
{
    return (new \ReflectionMethod($lock, 'refresh'))
        ->getDeclaringClass()
        ->getName() !== \Illuminate\Cache\Lock::class;
}

Try / catch

try {
    $lock->refresh(30);
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'does not support refreshing locks')) {
        // fall back: release and re-acquire
        $lock->release();
        $lock = Cache::lock($lock->owner() ? 'job-' . $id : 'job-' . $id, 30)->block(5);
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling $lock->refresh(60) on a lock acquired from a driver that doesn't support TTL extension (e.g., ArrayLock, or custom Lock subclass without overriding refresh). Also when calling refresh() on a lock restored via restoreLock() that lost its concrete subclass.

Common situations: Code designed for RedisLock deployed against the array driver in tests. Custom lock implementations that subclass Lock but forget to override refresh(). Long-running jobs that try to extend their lock mid-execution on an unsupported driver.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/691fe2d0a423641e. Report an issue: GitHub.

Appendix: source

Thrown at src/Illuminate/Cache/Lock.php:149

            try {
                return $callback();
            } finally {
                $this->release();
            }
        }

        return true;
    }

    /**
     * Attempt to refresh the lock for the given number of seconds.
     *
     * @param  int|null  $seconds
     * @return bool
     */
    public function refresh($seconds = null)
    {
        throw new RuntimeException('This lock driver does not support refreshing locks.');
    }

    /**
     * Returns the current owner of the lock.
     *
     * @return string
     */
    public function owner()
    {
        return $this->owner;
    }

    /**
     * Determine if the lock is currently held by any process.
     *
     * @return bool
     */
    public function isLocked(): bool

View on GitHub (pinned to e0f6eb3518)