cjpais/Handy · error · anyhow::Error
server sent more than the expected
Error message
server sent more than the expected {} bytes What it means
Raised when a server sends more bytes than the known total (catalog-pinned size or advertised Content-Length) — the cap check fires on the first chunk that would exceed it. This is a defense against misbehaving or hostile servers trying to fill the disk through an unbounded stream. Everything already written is considered tainted because the server provably lied, so the file handle is dropped and the partial deleted.
Solutions
- Retry — the tainted partial is already removed and the next attempt restarts cleanly.
- If one host consistently triggers it, switch to the other source (mirror <-> primary); this guard failing means that endpoint's responses cannot be trusted.
- Verify the URL serves the exact expected byte count with curl -I.
- Report the endpoint; over-long responses on a pinned-size object indicate a server-side fault.
Defensive patterns
Strategy: retry
Try / catch
match manager.download_model(model_id, None).await {
Ok(_) => Ok(()),
Err(e) if e.to_string().contains("more than the expected") => {
// over-long body: tainted partial deleted; clean retry, then switch source if repeated
manager.download_model(model_id, None).await
}
Err(e) => Err(e),
} Prevention
- Enforce a hard byte cap from the known total on every download loop (as this code does) so unbounded streams can't fill disks.
- Delete rather than bless any bytes written by a server that provably lied about size.
- Prefer hash-pinned catalogs so tampering servers are caught at verification even if size checks pass.
When it happens
Trigger: Server ignores Range and streams a full/different body while a size is pinned; server keeps the connection open past Content-Length (infinite/broken stream); a malicious mirror padding the response.
Common situations: Misconfigured CDNs appending trailers or serving error pages mid-stream; compromised or buggy mirrors; proxies injecting content.
Related errors
- no response within s from
- server advertises bytes, expected
- server returned Content-Range starting at
- Complete model file not found
- Download failed from Hugging Face
AI-assisted analysis of cjpais/Handy@c89b7bf389 (2026-08-17).
Data as JSON: /api/errors/1e4eb633b0ac40d7.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/managers/model/download.rs:347
DOWNLOAD_STALL_TIMEOUT.as_secs()
)),
Ok(None) => break,
Ok(Some(chunk)) => chunk?,
},
_ = cancel_token.cancelled() => {
// Keep the partial for resume; caller handles state cleanup.
return Ok(HttpDownloadOutcome::Cancelled);
}
};
// An untrusted server must not be able to fill the disk: cut the
// transfer at the first byte past the known total instead of
// trusting it to eventually close the stream. Everything written
// so far is tainted by a provably-misbehaving server — clear it.
if let Some(cap) = known_total {
if downloaded + chunk.len() as u64 > cap {
drop(file);
let _ = fs::remove_file(partial_path);
return Err(anyhow::anyhow!(
"server sent more than the expected {} bytes",
cap
));
}
}
file.write_all(&chunk)?;
downloaded += chunk.len() as u64;
if last_emit.elapsed() >= throttle {
emit_progress(downloaded);
last_emit = Instant::now();
}
}
file.flush()?;
drop(file);
emit_progress(downloaded);
if let Some(expected) = known_total {
let actual = partial_path.metadata()?.len();View on GitHub (pinned to c89b7bf389)