tonhowtf/omniget · error
Failed to read .torrent file
Error message
Failed to read .torrent file: {} What it means
Wraps a `tokio::fs::read` failure in `download` when the URL is actually a local path to a `.torrent` file. Before the read, the code checks `path.exists()` and the `.torrent` extension; the read itself then failed (I/O error), so the raw metainfo bytes could not be loaded for `AddTorrent::from_bytes`.
Solutions
- Read the wrapped `{}` IO error to distinguish permission-denied vs not-found vs is-a-directory
- Re-check that the file still exists and is a regular readable file right before download; re-pick the .torrent file if it moved
- Fix filesystem permissions (chmod/ACL) or run the app with access to the file's location
- Pass a magnet URI instead of a file path if local file access is problematic
Example fix
// before
let bytes = tokio::fs::read(path).await
.map_err(|e| anyhow::anyhow!("Failed to read .torrent file: {}", e))?;
// after
let bytes = match tokio::fs::read(path).await {
Ok(b) => b,
Err(e) => {
tracing::warn!("cannot read torrent file {}: {e}; falling back to magnet URL", path.display());
return Err(anyhow!("Torrent file unreadable: {e}. Re-select the .torrent file."));
}
}; Defensive patterns
Strategy: validation
Validate before calling
fn validate_torrent_file(path: &str) -> Result<(), String> {
let p = std::path::Path::new(path);
if !p.is_file() { return Err(format!("not a regular file: {path}")); }
if p.extension().map(|e| e != "torrent").unwrap_or(true) { return Err("missing .torrent extension".into()); }
std::fs::metadata(p).and_then(|m| m.permissions()).map_err(|e| e.to_string())?;
Ok(())
} Type guard
fn is_readable_torrent_path(url: &str) -> bool {
let p = std::path::Path::new(url);
p.is_file() && p.extension().map(|e| e == "torrent").unwrap_or(false)
} Try / catch
match downloader.download(&info, &opts, tx).await {
Err(e) if e.to_string().contains("Failed to read .torrent file") => {
Err(anyhow!("The .torrent file could not be read ({e}). Re-select or re-download the file."))
}
other => other,
} Prevention
- Re-check the file exists and is a regular file immediately before reading (avoid TOCTOU gaps)
- Check file permissions/ownership, especially on sandboxed macOS or Windows ACL-restricted locations
- Verify the file wasn't on an unmounted or disconnected volume at selection time
- Prefer magnet URIs when local file access is unreliable
When it happens
Trigger: Calling `download` with a local `.torrent` file path that passed the `exists()` check but failed to read: permission denied on the file, it's a directory named `x.torrent`, the file vanished between the exists-check and the read (TOCTOU), or a broken symlink.
Common situations: Torrent file moved/deleted after being picked in the UI; file on a disconnected network drive or unmounted volume; OS permission restrictions (e.g. macOS sandbox or Windows ACLs) blocking read; a folder misleadingly named `foo.torrent`.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- Failed to read .torrent file
- nao leu
- Failed to move into place
- Failed to replace
- Write error (disk full?)
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/c30c33ae5ace8ccc.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/platforms/magnet/mod.rs:178
*dir_guard = Some(output_dir.clone());
s
} else {
tracing::info!("[magnet] reusing existing session");
guard.as_ref().unwrap().clone()
}
};
let add_torrent = if url.starts_with("magnet:")
|| url.starts_with("http://")
|| url.starts_with("https://")
{
AddTorrent::from_url(url)
} else {
let path = std::path::Path::new(url);
if path.exists() && path.extension().map(|e| e == "torrent").unwrap_or(false) {
let bytes = tokio::fs::read(path)
.await
.map_err(|e| anyhow::anyhow!("Failed to read .torrent file: {}", e))?;
AddTorrent::from_bytes(bytes)
} else {
AddTorrent::from_url(url)
}
};
let only_files = opts
.torrent_files
.as_ref()
.filter(|v| !v.is_empty())
.cloned();
if let Some(sel) = &only_files {
tracing::info!("[magnet] selective download: {} file(s)", sel.len());
}
let trackers = if opts.torrent_auto_trackers {
let list = crate::core::trackers::extra_trackers();
tracing::info!("[magnet] injecting {} public trackers", list.len());
Some(list)
} else {View on GitHub (pinned to 8600b91f42)