{"record":{"id":"1e4eb633b0ac40d7","repo":"cjpais/Handy","slug":"server-sent-more-than-the-expected-bytes","errorCode":null,"errorMessage":"server sent more than the expected {} bytes","messagePattern":"server sent more than the expected (.+?) bytes","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model/download.rs","lineNumber":347,"sourceCode":"                        DOWNLOAD_STALL_TIMEOUT.as_secs()\n                    )),\n                    Ok(None) => break,\n                    Ok(Some(chunk)) => chunk?,\n                },\n                _ = cancel_token.cancelled() => {\n                    // Keep the partial for resume; caller handles state cleanup.\n                    return Ok(HttpDownloadOutcome::Cancelled);\n                }\n            };\n            // An untrusted server must not be able to fill the disk: cut the\n            // transfer at the first byte past the known total instead of\n            // trusting it to eventually close the stream. Everything written\n            // so far is tainted by a provably-misbehaving server — clear it.\n            if let Some(cap) = known_total {\n                if downloaded + chunk.len() as u64 > cap {\n                    drop(file);\n                    let _ = fs::remove_file(partial_path);\n                    return Err(anyhow::anyhow!(\n                        \"server sent more than the expected {} bytes\",\n                        cap\n                    ));\n                }\n            }\n            file.write_all(&chunk)?;\n            downloaded += chunk.len() as u64;\n            if last_emit.elapsed() >= throttle {\n                emit_progress(downloaded);\n                last_emit = Instant::now();\n            }\n        }\n        file.flush()?;\n        drop(file);\n        emit_progress(downloaded);\n\n        if let Some(expected) = known_total {\n            let actual = partial_path.metadata()?.len();","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/managers/model/download.rs#L329-L365","documentation":"Body-size cap enforcement: the server streamed past known_total (catalog-pinned size or advertised Content-Length). A server that overshoots its own contract is provably misbehaving, so the writer cuts the transfer at the first overflowing chunk, drops the file handle, and deletes everything written so far — preventing both disk-fill by an untrusted host and tainted data.","triggerScenarios":"Server sends an object larger than the pinned expected_size (upstream re-published a bigger file, stale catalog); server advertises a small Content-Length then streams more; a concatenating proxy.","commonSituations":"Catalog size skew after upstream updates; misconfigured mirrors serving bundles instead of single files.","solutions":["Refresh the catalog/app so expected_size matches the real object, then retry","Verify the real size: curl -sI <url> and compare Content-Length against the catalog","Switch to the official HuggingFace revision whose size is pinned","Report or stop using a mirror that consistently overshoots"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match downloader.download_http_resumable(...).await {\n    Ok(outcome) => Ok(outcome),\n    Err(e) if e.to_string().starts_with(\"server sent more than\") => {\n        // server overshoots the pinned size: metadata is stale or the host is bad\n        refresh_catalog_or_switch_source(model_id).await?;\n        downloader.download_http_resumable(...).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Verify a mirror serves exactly the pinned byte size with curl -sI before adding it","Pin sizes to immutable revisions so upstream re-publishes cannot overshoot","Escalate recurring overshoot to a source switch rather than endless retries"],"tags":["size-cap","overshoot","download-integrity","http"],"backgroundTag":"download-size-mismatch","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}