PrefectHQ/fastmcp · error · StateFileError

Could not lock CLI state

Error message

Could not lock CLI state

What it means

StateFileError raised by state_lock when acquiring the cross-process advisory lock fails with an OSError (StateFileError from _restrict_access is re-raised unchanged). The CLI cannot guarantee exclusive access to CLI state, so it aborts rather than risking concurrent corruption.

Source

Thrown at fastmcp_slim/fastmcp/cli/deploy/state.py:125

        if os.name == "nt":
            import msvcrt

            if lock_path.stat().st_size == 0:
                lock_file.write(b"\0")
                lock_file.flush()
            lock_file.seek(0)
            msvcrt.locking(lock_file.fileno(), msvcrt.LK_LOCK, 1)
        else:
            import fcntl

            fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
    except (OSError, StateFileError) as exc:
        if lock_file is not None:
            with suppress(OSError):
                lock_file.close()
        if isinstance(exc, StateFileError):
            raise
        raise StateFileError("Could not lock CLI state") from exc

    try:
        yield
    finally:
        if os.name == "nt":
            import msvcrt

            with suppress(OSError):
                lock_file.seek(0)
                msvcrt.locking(lock_file.fileno(), msvcrt.LK_UNLCK, 1)
        else:
            import fcntl

            with suppress(OSError):
                fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
        with suppress(OSError):
            lock_file.close()

View on GitHub (pinned to 1f02114297)

Solutions

  1. Find and terminate the stale process holding the lock (lsof <state-dir>/.state.lock), then retry
  2. Move FASTMCP_STATE_PATH off network filesystems (NFS/SMB) to a local disk
  3. Raise the open-file limit (ulimit -n) if it is exhausted
  4. Remove a leftover .state.lock only after confirming no CLI process is running
Defensive patterns

Strategy: retry

Validate before calling

import subprocess
result = subprocess.run(["lsof", str(state_dir / ".state.lock")], capture_output=True)
if result.stdout:
    raise RuntimeError("Lock held by another process; terminate it first")

Try / catch

import time

for attempt in range(5):
    try:
        with state_lock(state_dir):
            ...
        break
    except StateFileError:
        if attempt == 4:
            raise
        time.sleep(0.5 * (attempt + 1))

Prevention

When it happens

Trigger: Entering state_lock when lock_path.open('a+b') raises OSError (directory not writable, too many open files) or the platform flock/LockFileEx call fails — e.g. a second process holds an incompatible lock or the filesystem does not support locking.

Common situations: Another fastmcp CLI process already holds the lock (stuck/hung process); state directory on an NFS/SMB share without working advisory locks; ulimit -n too low; antivirus holding the file open on Windows.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/f4874bdbb3fc0563. Report an issue: GitHub.