lencx/ChatGPT · error

[view:download] Failed to lock download path

Error message

[view:download] Failed to lock download path

What it means

Panic from download_path.lock() returning Err(PoisonError) in the DownloadEvent::Requested arm. A std::sync::Mutex is poisoned when any thread panics while holding the guard; every later lock() then returns Err until recovered. The shared Arc<Mutex<PathBuf>> tracks the in-flight download path between the Requested and Finished events, and the other arms contain their own .expect() calls that can panic while state is being updated.

Source

Thrown at src-tauri/src/core/setup.rs:71

            // Wrap the window in Arc<Mutex<_>> to manage ownership across threads
            let window = Arc::new(Mutex::new(core_window));

            let main_view =
                WebviewBuilder::new("main", WebviewUrl::App("https://chatgpt.com".into()))
                    .auto_resize()
                    .on_download({
                        let app_handle = handle.clone();
                        let download_path = Arc::new(Mutex::new(PathBuf::new()));
                        move |_, event| {
                            match event {
                                DownloadEvent::Requested { destination, .. } => {
                                    let download_dir = app_handle
                                        .path()
                                        .download_dir()
                                        .expect("[view:download] Failed to get download directory");
                                    let mut locked_path = download_path
                                        .lock()
                                        .expect("[view:download] Failed to lock download path");
                                    *locked_path = download_dir.join(&destination);
                                    *destination = locked_path.clone();
                                }
                                DownloadEvent::Finished { success, .. } => {
                                    let final_path = download_path
                                        .lock()
                                        .expect("[view:download] Failed to lock download path")
                                        .clone();

                                    if success {
                                        app_handle
                                            .shell()
                                            .open(final_path.to_string_lossy(), None)
                                            .expect("[view:download] Failed to open file");
                                    }
                                }
                                _ => (),
                            }

View on GitHub (pinned to a6de9a8b61)

Solutions

  1. Recover from poisoning with match on lock() and poisoned.into_inner(), since a PathBuf is always safe to reuse even after a panic
  2. Eliminate the panics in the other arms (download_dir, shell open) so the mutex never gets poisoned in the first place
  3. Replace Mutex<PathBuf> with std::sync::atomic/lighter state or std::sync::mpsc if poisoning risk stays
  4. Run download-path updates with lock().unwrap_or_else(|p| p.into_inner()) at every call site

Example fix

// before
let mut locked_path = download_path
    .lock()
    .expect("[view:download] Failed to lock download path");

// after
let mut locked_path = match download_path.lock() {
    Ok(guard) => guard,
    Err(poisoned) => poisoned.into_inner(),
};
Defensive patterns

Strategy: fallback

Validate before calling

// health check before mutating shared state
fn lock_path(m: &Mutex<PathBuf>) -> std::sync::MutexGuard<PathBuf> {
    m.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
}

Type guard

fn is_healthy(m: &Mutex<PathBuf>) -> bool {
    !m.is_poisoned()
}

Try / catch

let mut locked_path = match download_path.lock() {
    Ok(guard) => guard,
    Err(poisoned) => poisoned.into_inner(),
};

Prevention

When it happens

Trigger: A panic anywhere while the download_path guard is held (e.g. a later event handler in the same closure panicking mid-update) poisons the mutex; then any subsequent download (next Requested event) hits lock().expect() and panics again, so downloads permanently fail until app restart.

Common situations: A first download triggers the Failed-to-open-file or Failed-to-get-download-directory panic; the user retries the download and immediately hits the poisoned lock; long-running desktop sessions where one poisoned shared mutex quietly disables the whole download feature.

Related errors


AI-assisted analysis of lencx/ChatGPT@a6de9a8b61 (2026-08-16). Data as JSON: /api/errors/dd498e6d85aaea4e. Report an issue: GitHub.