xtekky/gpt4free · error · TokenManagerError
LOCK_TIMEOUT
LOCK_TIMEOUT
Error message
Failed to acquire lock
What it means
TokenManagerError LOCK_TIMEOUT raised by SharedTokenManager.acquireLock (sharedTokenManager.py:246) after exhausting all attempts to create the lock file guarding credential refresh. The loop even includes stale-lock recovery — if the lock's mtime exceeds LOCK_TIMEOUT_MS it unlinks it — so this error means the lock was repeatedly recreated by a live holder or could not be removed (permissions).
Source
Thrown at g4f/Provider/qwen/sharedTokenManager.py:246
for _ in range(max_attempts):
try:
with open(lock_path, "w") as f:
f.write(lock_id)
return
except Exception:
try:
stat = os.stat(str(lock_path))
lock_age = int(time.time() * 1000) - int(stat.st_mtime * 1000)
if lock_age > LOCK_TIMEOUT_MS:
try:
await os.unlink(str(lock_path))
except Exception:
pass
except Exception:
pass
await asyncio.sleep(attempt_interval / 1000)
raise TokenManagerError(TokenError.LOCK_TIMEOUT, "Failed to acquire lock")
async def releaseLock(self, lock_path: Path):
try:
await os.unlink(str(lock_path))
except Exception:
pass
async def saveCredentialsToFile(self, credentials: dict):
file_path = self.getCredentialFilePath()
os.makedirs(file_path.parent, exist_ok=True)
with open(file_path, "w") as f:
f.write(json.dumps(credentials, indent=2))
stat = os.stat(str(file_path))
self.memory_cache["file_mod_time"] = int(stat.st_mtime * 1000)
def isTokenValid(self, credentials: dict) -> bool:
expiry_date = credentials.get("expiry_date")
if not expiry_date:View on GitHub (pinned to 973504e177)
Solutions
- Find and stop the process holding the lock (lsof / fuser on the lock path), or delete the stale lock file manually
- Reduce concurrent workers sharing one credentials file, or give each its own config dir
- On network filesystems, move the credentials dir to local disk
- Retry after the holder finishes — transient contention resolves itself
Defensive patterns
Strategy: retry
Validate before calling
from pathlib import Path
import time
def lock_is_stale(path, max_age_ms) -> bool:
try:
age_ms = time.time() * 1000 - Path(path).stat().st_mtime * 1000
return age_ms > max_age_ms
except OSError:
return True Try / catch
try:
creds = await manager.getValidCredentials(qwen_client)
except TokenManagerError as e:
if "Failed to acquire lock" in str(e.message):
await asyncio.sleep(2)
creds = await manager.getValidCredentials(qwen_client) # holder likely finished
else:
raise Prevention
- Limit concurrent processes sharing one credentials file — one refresher at a time
- Keep the credentials directory on local disk, not NFS
- Clean up stale lock files when processes crash (startup hygiene)
When it happens
Trigger: Another process/thread holds the lock file and keeps touching it longer than the total retry window; lock removal fails due to directory permissions; many g4f processes sharing one credentials file on a slow or network filesystem where mtime updates lag.
Common situations: Running several g4f workers against the same home directory; a crashed process left a lock on a filesystem where mtime doesn't advance (some network mounts); NFS latency making the stale detection ineffective.
Related errors
AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14).
Data as JSON: /api/errors/1f7fb002692177e2.
Report an issue: GitHub.