Skip to content

Starting a session fails due to user data directory #189

@JonathanWoollett-Light

Description

@JonathanWoollett-Light

Describe the bug

A simple program attempting to connect to chromedriver fails to create a session.

This is following up from jonhoo/fantoccini#301 since it doesn't appear related to this library but rather a lower level issue.

To reproduce

I have a simple Rust project which creates this behaviour.

Download chrome and chromedriver with:

# Install prerequisites
sudo apt install unzip

# Install chrome headerless (normal chrome also works but is slower)
wget https://storage.googleapis.com/chrome-for-testing-public/139.0.7258.154/linux64/chrome-headless-shell-linux64.zip
unzip chrome-headless-shell-linux64.zip
sudo mv chrome-headless-shell-linux64/chrome-headless-shell /usr/local/bin/chrome
sudo chmod +x /usr/local/bin/chrome

# Install ChromeDriver
wget https://storage.googleapis.com/chrome-for-testing-public/139.0.7258.154/linux64/chromedriver-linux64.zip
unzip chromedriver-linux64.zip
sudo mv chromedriver-linux64/chromedriver /usr/local/bin/chromedriver
sudo chmod +x /usr/local/bin/chromedriver

Then make sure you have Rust installed.

Run cargo new --name hello-fantoccini to create a new project.

Update the below files to match the contents below.

main.rs:

use reqwest::Url;
use std::{
    net::{Ipv4Addr, SocketAddr, SocketAddrV4},
    time::Duration,
};

struct ChromeDriver(std::process::Child);
impl Drop for ChromeDriver {
    fn drop(&mut self) {
        self.0.kill().unwrap();
        self.0.wait().unwrap();
    }
}

#[tokio::main]
async fn main() {
    let status = std::process::Command::new("chrome")
        .arg("--version")
        .output()
        .unwrap();
    assert!(
        status.status.success(),
        "Missing chrome installation `chrome --version`: {status:?}"
    );
    println!(
        "chrome installed:\n\t{:?}\n\t{:?}",
        std::str::from_utf8(&status.stdout),
        std::str::from_utf8(&status.stderr)
    );

    let status = std::process::Command::new("chromedriver")
        .arg("--version")
        .output()
        .unwrap();
    assert!(
        status.status.success(),
        "Missing chromedriver installation `chromedriver --version`: {status:?}"
    );
    println!(
        "chromedriver installed:\n\t{:?}\n\t{:?}",
        std::str::from_utf8(&status.stdout),
        std::str::from_utf8(&status.stderr)
    );

    let chromedriver_port = 4470;
    let chromedriver = ChromeDriver(
        std::process::Command::new("chromedriver")
            .arg(format!("--port={chromedriver_port}"))
            .spawn()
            .unwrap(),
    );
    println!("chromedriver started");

    const TIMEOUT: Duration = Duration::from_secs(30);
    const SLEEP: Duration = Duration::from_millis(100);
    let timeout = std::time::Instant::now();
    let client = reqwest::Client::new();
    let url = Url::parse(&format!(
        "http://{}",
        SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, chromedriver_port))
    ))
    .unwrap();
    loop {
        assert!(timeout.elapsed() <= TIMEOUT, "timeout");
        let response = client.get(url.join("/status").unwrap()).send().await;
        match response {
            Ok(res) if res.status().is_success() => break,
            _ => {}
        }
        tokio::time::sleep(SLEEP).await;
    }
    println!("chromedriver running");

    let user_data_dir = tempfile::tempdir().unwrap();
    let user_data_arg = format!("--user-data-dir={}", user_data_dir.path().to_string_lossy());

    let json = serde_json::json!({
        "capabilities": {
            "alwaysMatch": {
                "browserName": "chrome",
                "goog:chromeOptions": {
                    "binary": "/usr/local/bin/chrome",
                    "args": [
                        user_data_arg,
                        "--headless=new",
                        "--no-sandbox",
                        "--disable-dev-shm-usage"
                    ]
                }
            },
            "firstMatch": [{}]
        }
    });
    let res = client
        .post(url.join("/session").unwrap())
        .json(&json)
        .send()
        .await
        .unwrap();
    println!("create session: {res:?}");
    println!("create session: {:?}", res.text().await.unwrap());

    drop(chromedriver);
    drop(user_data_dir);
}

cargo.toml:

[package]
name = "hello-fantoccini"
version = "0.1.0"
edition = "2024"

[dependencies]
reqwest = { version = "0.12.7", features = ["cookies", "json", "stream", "multipart", "native-tls-vendored"] }
tokio = { version = "1.42.0", features = ["full"] }
serde_json = "1.0.143"
tempfile = "3.15.0"

Run with cargo run.

Which should produce an output like

chrome installed:
        Ok("Google Chrome for Testing 139.0.7258.154\n")
        Ok("")
chromedriver installed:
        Ok("ChromeDriver 139.0.7258.154 (9e0d6b2b47ffb17007b713429c9a302f9e43847f-refs/branch-heads/7258@{#2926})\n")
        Ok("")
chromedriver started
Starting ChromeDriver 139.0.7258.154 (9e0d6b2b47ffb17007b713429c9a302f9e43847f-refs/branch-heads/7258@{#2926}) on port 4470
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully on port 4470.
chromedriver running
create session: Response { url: "http://127.0.0.1:4470/session", status: 500, headers: {"content-length": "882", "content-type": "application/json; charset=utf-8", "cache-control": "no-cache"} }
create session: "{\"value\":{\"error\":\"session not created\",\"message\":\"session not created: probably user data directory is already in use, please specify a unique value for --user-data-dir argument, or don't use --user-data-dir\",\"stacktrace\":\"#0 0x55ef0dce808a \\u003Cunknown>\\n#1 0x55ef0d787a70 \\u003Cunknown>\\n#2 0x55ef0d7c2e07 \\u003Cunknown>\\n#3 0x55ef0d7bd5a7 \\u003Cunknown>\\n#4 0x55ef0d80d93e \\u003Cunknown>\\n#5 0x55ef0d80cf06 \\u003Cunknown>\\n#6 0x55ef0d7ff1b3 \\u003Cunknown>\\n#7 0x55ef0d7cb59b \\u003Cunknown>\\n#8 0x55ef0d7cc971 \\u003Cunknown>\\n#9 0x55ef0dcad25b \\u003Cunknown>\\n#10 0x55ef0dcb0fa9 \\u003Cunknown>\\n#11 0x55ef0dc94339 \\u003Cunknown>\\n#12 0x55ef0dcb1b58 \\u003Cunknown>\\n#13 0x55ef0dc78c1f \\u003Cunknown>\\n#14 0x55ef0dcd5118 \\u003Cunknown>\\n#15 0x55ef0dcd52f6 \\u003Cunknown>\\n#16 0x55ef0dce7066 \\u003Cunknown>\\n#17 0x70813489caa4 \\u003Cunknown>\\n#18 0x708134929c3c \\u003Cunknown>\\n\"}}"

Removing "goog:chromeOptions" and re-running produce the same outcome.

uname -a outputs:

Linux DESKTOP-UTVQJHQ 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

Expected behavior
It should successfully create a new session.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions