{"record":{"id":"af7739583ad66350","repo":"tonhowtf/omniget","slug":"failed-to-read-torrent-file","errorCode":null,"errorMessage":"Failed to read .torrent file: {}","messagePattern":"Failed to read \\.torrent file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/magnet.rs","lineNumber":178,"sourceCode":"                *dir_guard = Some(output_dir.clone());\n                s\n            } else {\n                tracing::info!(\"[magnet] reusing existing session\");\n                guard.as_ref().unwrap().clone()\n            }\n        };\n\n        let add_torrent = if url.starts_with(\"magnet:\")\n            || url.starts_with(\"http://\")\n            || url.starts_with(\"https://\")\n        {\n            AddTorrent::from_url(url)\n        } else {\n            let path = std::path::Path::new(url);\n            if path.exists() && path.extension().map(|e| e == \"torrent\").unwrap_or(false) {\n                let bytes = tokio::fs::read(path)\n                    .await\n                    .map_err(|e| anyhow::anyhow!(\"Failed to read .torrent file: {}\", e))?;\n                AddTorrent::from_bytes(bytes)\n            } else {\n                AddTorrent::from_url(url)\n            }\n        };\n        let only_files = opts\n            .torrent_files\n            .as_ref()\n            .filter(|v| !v.is_empty())\n            .cloned();\n        if let Some(sel) = &only_files {\n            tracing::info!(\"[magnet] selective download: {} file(s)\", sel.len());\n        }\n        let trackers = if opts.torrent_auto_trackers {\n            let list = crate::core::trackers::extra_trackers();\n            tracing::info!(\"[magnet] injecting {} public trackers\", list.len());\n            Some(list)\n        } else {","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/magnet.rs#L160-L196","documentation":"When the download `url` is not a magnet URI, download treats it as a path to a .torrent file. It checks that the path exists and has extension \".torrent\", then reads the bytes with tokio::fs::read; any I/O error during that read is wrapped in this error with the OS reason. The file passed the exists/extension check but could not actually be read.","triggerScenarios":"Calling download with a local .torrent path where tokio::fs::read fails — permission denied, file deleted between the exists() check and the read, path is a directory named x.torrent, or an IO error on a network/removable drive.","commonSituations":"File moved/deleted after being added to the UI (TOCTOU between exists() and read()); downloading on a machine where the .torrent lives on an unavailable drive; restrictive ACLs; passing a directory or a file without read permission.","solutions":["Read the wrapped `{}` OS error — permission denied vs not-found dictates the fix.","Verify the .torrent file still exists and is readable by the app user (ls -l / icacls).","Confirm the path is a regular file, not a directory, and is on a mounted, available volume.","Handle the error in the caller by prompting the user to re-select the .torrent file.","Prefer opening the file once (File::open) and reporting an explicit 'file missing' error instead of relying on the exists() check."],"exampleFix":"// before\nlet bytes = tokio::fs::read(path).await\n    .map_err(|e| anyhow::anyhow!(\"Failed to read .torrent file: {}\", e))?;\n// after\nlet bytes = tokio::fs::read(path).await.map_err(|e| {\n    if e.kind() == std::io::ErrorKind::NotFound {\n        anyhow::anyhow!(\"Torrent file no longer exists: {}\", path.display())\n    } else {\n        anyhow::anyhow!(\"Failed to read .torrent file: {}\", e)\n    }\n})?;","handlingStrategy":"validation","validationCode":"// Rust: validate the torrent path before calling download\nfn validate_torrent_path(path: &str) -> Result<(), String> {\n    let p = std::path::Path::new(path);\n    if !p.is_file() { return Err(format!(\"not a regular file: {}\", path)); }\n    if p.extension().map(|e| e != \"torrent\").unwrap_or(true) { return Err(\"extension is not .torrent\".into()); }\n    std::fs::File::open(p).map(|_| ()).map_err(|e| format!(\"unreadable: {}\", e))\n}","typeGuard":null,"tryCatchPattern":"match download(path, opts).await {\n    Err(e) if e.to_string().contains(\"Failed to read .torrent file\") => {\n        // file vanished or unreadable; prompt user to re-select the .torrent\n        ui.prompt_reselect_torrent(path);\n        Err(e)\n    }\n    other => other,\n}","preventionTips":["Verify the .torrent file exists and is readable right before adding it to the queue","Don't move or delete .torrent files while a download is pending","Keep .torrent files on a local, always-mounted volume rather than network/removable drives","Check file permissions for the user account running the app"],"tags":["torrent","filesystem","io","file-read"],"backgroundTag":"file-read-failed","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}