{"record":{"id":"825ce067f8385d41","repo":"jdx/mise","slug":"remote-cache-blob-pack-has-invalid-magic","errorCode":null,"errorMessage":"remote cache blob pack has invalid magic","messagePattern":"remote cache blob pack has invalid magic","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/lib.rs","lineNumber":899,"sourceCode":"    let item_units = digests.len().div_ceil(BLOB_PACK_TIMEOUT_ITEMS_PER_UNIT);\n    let item_units = u64::try_from(item_units).unwrap_or(u64::MAX);\n    let multiplier = byte_units.max(item_units).max(1);\n    base.saturating_mul(u32::try_from(multiplier).unwrap_or(u32::MAX))\n}\n\nasync fn decode_blob_pack(\n    response: reqwest::Response,\n    requested: &[CacheDigest],\n    staging_dir: &Path,\n) -> Result<DownloadedBlobPack> {\n    let metadata = BlobPackResponseMetadata::from_headers(response.headers())?;\n    let requested = requested.iter().cloned().collect::<BTreeSet<_>>();\n    let stream = response.bytes_stream().map_err(std::io::Error::other);\n    let mut reader = tokio_util::io::StreamReader::new(stream);\n    let mut magic = [0_u8; BLOB_PACK_MAGIC.len()];\n    reader.read_exact(&mut magic).await?;\n    if &magic != BLOB_PACK_MAGIC {\n        bail!(\"remote cache blob pack has invalid magic\");\n    }\n\n    let directory = tempfile::tempdir_in(staging_dir)?;\n    let mut seen = BTreeSet::new();\n    let mut blobs = Vec::new();\n    let mut payload_bytes = 0_u64;\n    let mut framed_bytes = BLOB_PACK_MAGIC.len() as u64;\n    loop {\n        let mut algorithm = [0_u8; 1];\n        if reader.read(&mut algorithm).await? == 0 {\n            break;\n        }\n        let (algorithm, mut hasher) = match algorithm[0] {\n            1 => (\n                \"blake3\",\n                BlobPackHasher::Blake3(Box::new(blake3::Hasher::new())),\n            ),\n            2 => (\"sha256\", BlobPackHasher::Sha256(sha2::Sha256::new())),","sourceCodeStart":881,"sourceCodeEnd":917,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/lib.rs#L881-L917","documentation":"Thrown by decode_blob_pack (crates/mise-cache-core/src/lib.rs:899): every blob pack body must start with the 8-byte magic BLOB_PACK_MAGIC = b\"MISEPK01\" (lib.rs:46). The decoder reads exactly 8 bytes and bails if they differ — the response passed the Content-Type check (error 53) but the body is not actually the framed pack format.","triggerScenarios":"Calling the blob-pack download path where the body starts with something other than MISEPK01 — e.g. the server sends uncompressed JSON or raw concatenated blobs, sends a newer pack format (MISEPK02) after a protocol bump, or a proxy/error page replaced the body while preserving the 200 status and vendor Content-Type.","commonSituations":"Version skew: server writes a newer pack container than the client decodes; partially implemented server that sets headers but streams a different serialization; intermediaries substituting bodies; truncated responses where the first 8 bytes come from an error payload.","solutions":["Align client and server versions — a magic mismatch almost always means a different container format generation","Capture the first bytes of the offending response server-side to confirm what is actually being sent instead of the pack framing","Verify no intermediary (proxy, WAF, gateway) is substituting the response body on the pack route","If you operate the server, emit the MISEPK01 header bytes exactly before the record stream"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// cheap preflight if you control the server: expose a format version in capabilities\n// and refuse to request packs when it differs from the client's pack generation","typeGuard":null,"tryCatchPattern":"match client.get_blob_pack(&digests, &staging).await {\n    Err(e) if e.to_string().contains(\"invalid magic\") => {\n        client.disable_blob_packs(); // format generation mismatch — packs unusable\n        fallback_per_blob(&client, &digests)\n    }\n    other => other?,\n}","preventionTips":["Bump PROTOCOL_VERSION (and pack magic) together so old clients fail the capability check instead of the magic check","Never serve a non-pack body on the pack route, even for errors — use proper status codes","Fall back to per-blob downloads on any pack decode failure; they are independently verified"],"tags":["mise","cache","integrity","blob-pack","wire-format"],"backgroundTag":"invalid-magic-bytes","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}