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.

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.

Appendix: 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)