PrefectHQ/fastmcp · error · StateFileError
The CLI state lock must not be a symbolic link
Error message
The CLI state lock must not be a symbolic link
What it means
StateFileError raised by state_lock when the lock file .state.lock inside the state directory is a symbolic link. A symlinked lock could redirect writes outside the state directory (a tampering/TOCTOU risk), so the CLI refuses to take the lock.
Source
Thrown at fastmcp_slim/fastmcp/cli/deploy/state.py:101
except OSError as exc:
raise StateFileError("Could not restrict access to CLI state") from exc
def _prepare_directory(path: Path) -> None:
try:
path.mkdir(parents=True, exist_ok=True)
except OSError as exc:
raise StateFileError("Could not create the CLI state directory") from exc
_restrict_access(path, directory=True)
@contextmanager
def state_lock(directory: Path) -> Iterator[None]:
"""Lock related CLI state changes across processes."""
_prepare_directory(directory)
lock_path = directory / ".state.lock"
if lock_path.is_symlink():
raise StateFileError("The CLI state lock must not be a symbolic link")
lock_file = None
try:
lock_file = lock_path.open("a+b")
_restrict_access(lock_path)
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:View on GitHub (pinned to 1f02114297)
Solutions
- Inspect and remove the link: ls -l <state-dir>/.state.lock then rm <state-dir>/.state.lock
- Ensure the state directory is only writable by your user (0o700)
- Exclude the state directory from sync/backup tools that may recreate symlinks
- Retry the CLI command after removing the link
Example fix
// before $ ls -l ~/.fastmcp/state/.state.lock lrwxr-xr-x .state.lock -> /tmp/evil // after $ rm ~/.fastmcp/state/.state.lock $ chmod 700 ~/.fastmcp/state
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
lock_path = state_dir / ".state.lock"
if lock_path.is_symlink():
raise RuntimeError(
f"{lock_path} is a symlink; remove it and secure the directory"
) Type guard
def is_safe_lockfile(path: Path) -> bool:
return path.exists() is False or not path.is_symlink() Try / catch
try:
with state_lock(state_dir):
...
except StateFileError:
(state_dir / ".state.lock").unlink(missing_ok=True)
with state_lock(state_dir):
... Prevention
- chmod 700 the state directory so other users cannot plant links
- Exclude the state directory from git/sync tools that follow or create symlinks
- Periodically audit the state dir: find ~/.fastmcp -type l
When it happens
Trigger: Entering state_lock (via _load_session_snapshot, set_api_origin, save_for_origin, clear_if_matches) after something replaced directory/.state.lock with a symlink — attacker tampering, a leftover link from a broken sync/backup tool, or a malicious shared-directory setup.
Common situations: State directory synced by Dropbox/OneDrive/git that restored a symlink; multi-user /tmp-style shared directory where another account planted the link; a previous crash or third-party script created the link.
Related errors
- CLI state must not be a symbolic link: {path.name}
- Could not lock CLI state
- Could not restrict access to CLI state
- Could not create the CLI state directory
- Could not read CLI state: {path.name}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/9009a15959607e71.
Report an issue: GitHub.