xtekky/gpt4free · error · TimeoutError

Nodriver is already in use, please try again later.

Error message

Nodriver is already in use, please try again later.

What it means

Raised by get_nodriver() in g4f/requests/__init__.py when a per-user-data-dir lock file exists and did not disappear within the whole timeout window (it polls once per second for `timeout` seconds). g4f serializes nodriver access with a lock file because two Browser instances sharing the same user_data_dir corrupt the profile, so a stale or genuinely held lock eventually produces this TimeoutError.

Source

Thrown at g4f/requests/__init__.py:322

        lock_file.parent.mkdir(exist_ok=True)
        # Implement a short delay (milliseconds) to prevent race conditions.
        await asyncio.sleep(0.1 * random.randint(0, 50))
        if lock_file.exists():
            opend_at = float(lock_file.read_text())
            time_open = time.time() - opend_at
            if timeout * 2 > time_open:
                debug.log(
                    f"Nodriver: Browser is already in use since {time_open} secs."
                )
                debug.log("Lock file:", lock_file)
                for idx in range(timeout):
                    if lock_file.exists():
                        await asyncio.sleep(1)
                    else:
                        break
                    if idx == timeout - 1:
                        debug.log("Timeout reached, nodriver is still in use.")
                        raise TimeoutError(
                            "Nodriver is already in use, please try again later."
                        )
            else:
                debug.log(
                    f"Nodriver: Browser was opened {time_open} secs ago, closing it."
                )
                await BrowserConfig.stop_browser()
                lock_file.unlink(missing_ok=True)
        lock_file.write_text(str(time.time()))
        debug.log(f"Open nodriver with user_dir: {user_data_dir}")
    try:
        browser_args = kwargs.pop("browser_args", None) or ["--no-sandbox"]

        if BrowserConfig.port:
            browser_executable_path = "/bin/google-chrome"
        browser = await nodriver.start(
            user_data_dir=user_data_dir,
            browser_args=[*browser_args, f"--proxy-server={proxy}"]

View on GitHub (pinned to 973504e177)

Solutions

  1. Retry later, as the message suggests — the other holder usually releases the browser when done.
  2. Give each concurrent worker its own user_data_dir: get_nodriver(user_data_dir=f"worker-{worker_id}") so locks do not collide.
  3. Delete the stale lock file (path is printed by the adjacent debug.log 'Lock file:' line, typically under the platformdirs user_config_dir 'g4f-nodriver') after confirming no browser is actually running.
  4. Increase the timeout argument so legitimate long sessions do not trip it.

Example fix

// before
browser, stop = await get_nodriver()  # shared default dir -> lock contention

// after
browser, stop = await get_nodriver(user_data_dir=f"worker-{os.getpid()}", timeout=600)
Defensive patterns

Strategy: retry

Try / catch

try:
    browser, stop = await get_nodriver(timeout=120)
except TimeoutError:
    # another holder has the profile; back off and retry, or use a separate dir
    await asyncio.sleep(30)
    browser, stop = await get_nodriver(user_data_dir=f"retry-{time.time()}")

Prevention

When it happens

Trigger: Two concurrent requests both call get_nodriver with the same user_data_dir while the first browser is still open longer than `timeout` seconds; or a previous run crashed without removing its lock file, leaving a stale lock whose timestamp is newer than timeout*2 seconds logic did not trigger cleanup.

Common situations: Running g4f in multiple worker processes (gunicorn/uvicorn workers) that all default to the same 'nodriver' profile dir; a hard kill (OOM, SIGKILL, power loss) leaving the lock file behind; long Cloudflare challenges keeping the browser open past the timeout.

Related errors


AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14). Data as JSON: /api/errors/c0db8eef998af7a4. Report an issue: GitHub.