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
- Recover from poisoning with match on lock() and poisoned.into_inner(), since a PathBuf is always safe to reuse even after a panic
- Eliminate the panics in the other arms (download_dir, shell open) so the mutex never gets poisoned in the first place
- Replace Mutex<PathBuf> with std::sync::atomic/lighter state or std::sync::mpsc if poisoning risk stays
- 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
- Remove every .expect()/unwrap that can panic while the guard is held — poison starts with a panic
- Keep mutex critical sections short: clone data out, drop the guard, then do fallible work
- For simple state like PathBuf, always recover via into_inner() instead of propagating poison
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
- [core:window] Failed to build window
- [core:window] Failed to get window size
- [view:download] Failed to get download directory
- [view:download] Failed to open file
- error while running lencx/ChatGPT application
AI-assisted analysis of lencx/ChatGPT@a6de9a8b61 (2026-08-16).
Data as JSON: /api/errors/dd498e6d85aaea4e.
Report an issue: GitHub.