{"record":{"id":"de023c8acd737093","repo":"jdx/mise","slug":"server-returned-inconsistent-partial-content","errorCode":null,"errorMessage":"server returned inconsistent partial content","messagePattern":"server returned inconsistent partial content","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/http.rs","lineNumber":1043,"sourceCode":"                        }\n                        restarted_without_resume = true;\n                        continue;\n                    };\n                    let validator = response_validator(resp.headers());\n                    let response_length_matches = resp\n                        .content_length()\n                        .is_none_or(|length| length == end - start + 1);\n                    if start != offset\n                        || end + 1 != total\n                        || !response_length_matches\n                        || state.total_size.is_some_and(|expected| expected != total)\n                        || validator\n                            .as_ref()\n                            .is_some_and(|value| !value.matches(&state.validator))\n                    {\n                        partial.clear()?;\n                        if restarted_without_resume {\n                            bail!(\"server returned inconsistent partial content\");\n                        }\n                        restarted_without_resume = true;\n                        continue;\n                    }\n                    // Keep the filename associated with the bytes already on disk.\n                    // A redirect target may change between requests even when the\n                    // server accepts the validator and Range header. Replacing the\n                    // stored hint could make the completed bytes use a different\n                    // archive format than the response that started the partial.\n                    let effective_filename = state.effective_filename;\n                    let validator = validator.unwrap_or(state.validator);\n                    (\n                        offset,\n                        Some(total),\n                        Some(validator),\n                        true,\n                        effective_filename,\n                    )","sourceCodeStart":1025,"sourceCodeEnd":1061,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/http.rs#L1025-L1061","documentation":"On resume, the server's 206 response is validated against the recorded partial-download state: the range must start at the saved offset, end at the saved total size, the content length must match, and any ETag/validator must match. If any check fails, mise restarts once without resume; if the retry is also inconsistent, it aborts with this error rather than appending bytes from a changed or different file.","triggerScenarios":"Resuming a .part download where the remote file changed between sessions (new ETag/Last-Modified), the server returns a range not starting at the requested offset, the total size differs from the recorded one, or the Content-Length does not match end-start+1 — and a fresh restart hits the same mismatch.","commonSituations":"Resuming a download after the upstream released a new version at the same URL; a load-balanced pool serving different artifacts; an interrupted download left stale for days before retrying.","solutions":["Delete the partial download and its state file so mise fetches the file from scratch.","Verify the URL still points at the same artifact; re-pin the tool version if the remote was updated.","Retry later if the server is mid-rollout and serving inconsistent content across requests."],"exampleFix":"// before: stale .part from an older release being resumed\n# nothing to edit — stale partial state\n// after: clear partial state and reinstall\nrm -rf \"$(mise cache_dir)/downloads\"/<artifact>*.part*; mise install node@22","handlingStrategy":"fallback","validationCode":"if let Some((etag, total)) = saved_state {\n    let head = client.head(url).send().await?;\n    if head.etag() != Some(&etag) || head.content_length() != Some(total) {\n        delete_partial(); // remote changed; resume would be invalid\n    }\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = download().await {\n    if e.to_string().contains(\"inconsistent partial content\") {\n        delete_partial_download();\n        download().await?; // restart clean\n    } else { return Err(e); }\n}","preventionTips":["Pin tool versions so the remote artifact at a URL does not change under you.","Delete old .part files after long gaps before retrying an install.","Prefer mirrors with immutable, content-addressed artifact URLs."],"tags":["network","download","http","resume"],"backgroundTag":"checksum-mismatch","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}