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

  1. Find and stop the process holding the lock (lsof / fuser on the lock path), or delete the stale lock file manually
  2. Reduce concurrent workers sharing one credentials file, or give each its own config dir
  3. On network filesystems, move the credentials dir to local disk
  4. 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

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.