tonhowtf/omniget · error · anyhow::Error
stream error: {}
Error message
stream error: {} What it means
Thrown in download_streaming when the response body stream itself yields an error (Ok(Some(Err(e))) from the bytes_stream). This means the HTTP connection broke mid-download — the transport or hyper layer produced an I/O error while reading a chunk — as opposed to a timeout (handled separately) or a bad status code. The .part file retains everything downloaded before the failure and can be used for resume.
Solutions
- Retry the download with resume enabled so the .part file is continued rather than restarted (Range request / sidecar resume).
- Wrap the download call in a retry loop with exponential backoff for transient network errors.
- Reduce exposure to idle-connection kills by lowering chunk read time or verifying the server's keep-alive/timeout settings.
- Inspect the wrapped io/hyper error (the {} in the message) for the precise cause (e.g. ConnectionReset, UnexpectedEof) and address it specifically.
- Test with a wired/stable network or different DNS/VPN to rule out local network instability.
Example fix
// before
fetcher.download(&mut progress_tx).await?; // one shot, no resume
// after
for attempt in 0..5 {
match fetcher.download(&mut progress_tx).await {
Ok(()) => break,
Err(e) if attempt < 4 && is_transient_stream_error(&e) => {
tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;
}
Err(e) => return Err(e),
}
} Defensive patterns
Strategy: retry
Validate before calling
// sanity-check reachability before a long transfer
let resp = client.get(&url).send().await?;
if !resp.status().is_success() {
bail!("source unreachable: {}", resp.status());
} Try / catch
match fetcher.download(&mut tx).await {
Err(e) if e.to_string().starts_with("stream error") => {
// transient mid-stream failure: resume from .part with backoff
tokio::time::sleep(Duration::from_secs(2)).await;
fetcher.download_resuming(&mut tx).await?;
}
other => other?,
} Prevention
- Enable resume (.part continuation) so stream breaks don't restart from zero.
- Retry transient stream errors with exponential backoff (3-5 attempts).
- Prefer stable wired networks for very large transfers; avoid VPN/proxy hops when possible.
- Check the wrapped cause (ConnectionReset, UnexpectedEof, etc.) to pick the right remedy.
When it happens
Trigger: resp.bytes_stream() returns an Err item during the loop: connection reset by peer (RST), TLS handshake renegotiation failure, premature EOF (server closed connection without completing Content-Length), HTTP/2 GOAWAY, or proxy dropped the connection mid-transfer.
Common situations: Flaky Wi-Fi/mobile network or VPN drop mid-download; server or load balancer idle-killing long transfers; CDN edge node restarting; corporate proxy terminating long-lived connections; HTTP/2 stream reset by the server.
Related errors
- read timed out after {:?}
- download falhou: HTTP {}
- download de {} falhou: HTTP {}
- Torrent download failed: {}
- Failed to download attachment: {}
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/8a92e06d00cb3261.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/http_fetcher.rs:375
None => (
((downloaded as f64 / (downloaded as f64 + 500_000.0)) * 100.0)
.min(95.0),
None,
),
};
let _ = progress_tx
.send(ProgressUpdate::rich(
pct,
Some(downloaded),
total.filter(|t| *t > 0),
speed,
eta,
))
.await;
last_emit = std::time::Instant::now();
}
}
Ok(Some(Err(e))) => return Err(anyhow!("stream error: {}", e)),
Ok(None) => break,
Err(_) => {
return Err(anyhow!(
"read timed out after {:?}",
self.config.read_timeout
))
}
}
}
file.flush().await?;
drop(file);
tokio::fs::rename(part_path, &self.output_path).await?;
let _ = progress_tx.send(ProgressUpdate::percent(100.0)).await;
Ok(HttpFetcherResult {
bytes_written: downloaded,
})
}View on GitHub (pinned to 8600b91f42)