calesthio/OpenMontage · error · TimeoutError

ClipCache: could not acquire lock at {self.lock_path} after

Error message

ClipCache: could not acquire lock at {self.lock_path} after {timeout}s

What it means

Raised by ClipCache's lock context manager when an O_CREAT|O_EXCL lockfile at self.lock_path could not be created within the timeout window (polling every 50ms). Some other process holds the cache lock — typically a long cache write, index rebuild, or a crashed process that left a stale lockfile behind.

Source

Thrown at tools/video/clip_cache.py:245

                yield
            return

        # Fallback: O_EXCL create-file lock.
        deadline = time.time() + timeout
        acquired = False
        while time.time() < deadline:
            try:
                fd = os.open(
                    str(self.lock_path),
                    os.O_CREAT | os.O_EXCL | os.O_WRONLY,
                )
                os.close(fd)
                acquired = True
                break
            except FileExistsError:
                time.sleep(0.05)
        if not acquired:
            raise TimeoutError(
                f"ClipCache: could not acquire lock at {self.lock_path} "
                f"after {timeout}s"
            )
        try:
            yield
        finally:
            try:
                os.unlink(self.lock_path)
            except OSError:
                pass

    # ------------------------------------------------------------------
    # Manifest I/O (caller holds the lock)
    # ------------------------------------------------------------------

    def _read_manifest(self) -> dict[str, CacheEntry]:
        """Read the manifest file into a dict keyed by clip_id.

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Check for a live holder (ps/lsof on the lock's owner) — if none, delete the stale lockfile at the lock_path named in the message and retry
  2. Serialize cache access across processes (single writer, or a queue) instead of racing
  3. If concurrent access is legitimate, raise the timeout passed to the lock acquisition
  4. Investigate why the holder takes >timeout: large corpus writes or slow storage

Example fix

# before (two shells)
$ om clip build &   $ om clip search 'ocean'   # second times out

# after
$ flock cache.lock -c 'om clip build' && om clip search 'ocean'
# or remove stale lock: rm <lock_path from error message>, then retry
Defensive patterns

Strategy: retry

Validate before calling

import os, time
lock = Path(cache.lock_path)
if lock.exists():
    age = time.time() - lock.stat().st_mtime
    holder_alive = any('om' in (p.info or '') for p in psutil.Process().children(recursive=True))  # or pgrep your writer
    if age > 300 and not holder_alive:
        lock.unlink()  # stale lock cleanup before calling the tool

Try / catch

for attempt in range(3):
    try:
        with clip_cache.locked(timeout=30):
            clip_cache.write(records)
        break
    except TimeoutError:
        if attempt == 2:
            raise
        time.sleep(2 ** attempt)

Prevention

When it happens

Trigger: Two processes (e.g. two agent runs or a UI plus a CLI) hitting the same ClipCache concurrently and the first holding the lock longer than the timeout; a previous run killed mid-write leaving the lockfile on disk with no owner to remove it.

Common situations: Parallel cron jobs or CI shards sharing a cache directory; SIGKILL/power loss during a cache mutation; NFS or synced folders where unlink of the lock is delayed; extremely slow disks making a legitimate write exceed the timeout.

Understand the failure class

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/c94475c6b46527fc. Report an issue: GitHub.