{"record":{"id":"e0088004a8e8eda8","repo":"xai-org/grok-build","slug":"decoded-artifact-exceeds-the-max-decoded-bytes-b","errorCode":null,"errorMessage":"decoded artifact exceeds the {MAX_DECODED_BYTES}-byte cap","messagePattern":"decoded artifact exceeds the (.+?)-byte cap","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-update/src/auto_update.rs","lineNumber":1388,"sourceCode":"\n    let bin_tmp = tmp_download_path(dest);\n    let (comp_in, bin_out) = (comp_tmp.clone(), bin_tmp.clone());\n    let decoded = tokio::task::spawn_blocking(move || -> Result<()> {\n        use std::io::Read as _;\n        let src = std::fs::File::open(&comp_in)\n            .with_context(|| format!(\"open compressed download {}\", comp_in.display()))?;\n        let decoder: Box<dyn std::io::Read> = match codec {\n            Codec::Zstd => {\n                Box::new(zstd::stream::read::Decoder::new(src).context(\"init zstd decoder\")?)\n            }\n            Codec::Gzip => Box::new(flate2::read::GzDecoder::new(src)),\n        };\n        let mut out = std::fs::File::create(&bin_out)\n            .with_context(|| format!(\"create decoded binary {}\", bin_out.display()))?;\n        let mut capped = decoder.take(MAX_DECODED_BYTES + 1);\n        let written = std::io::copy(&mut capped, &mut out).context(\"decode\")?;\n        if written > MAX_DECODED_BYTES {\n            anyhow::bail!(\"decoded artifact exceeds the {MAX_DECODED_BYTES}-byte cap\");\n        }\n        Ok(())\n    })\n    .await;\n    let _ = tokio::fs::remove_file(&comp_tmp).await;\n\n    match decoded {\n        Ok(Ok(())) => publish_downloaded_artifact(&bin_tmp, dest).await,\n        Ok(Err(e)) => {\n            let _ = tokio::fs::remove_file(&bin_tmp).await;\n            Err(e)\n        }\n        Err(e) => {\n            let _ = tokio::fs::remove_file(&bin_tmp).await;\n            Err(anyhow::anyhow!(\"decode task panicked: {e}\"))\n        }\n    }\n}","sourceCodeStart":1370,"sourceCodeEnd":1406,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-update/src/auto_update.rs#L1370-L1406","documentation":"download_and_decode decodes a compressed artifact (e.g. gz/zstd) into the target binary. To prevent a decompression bomb from filling the disk, the decoder is capped with take(MAX_DECODED_BYTES + 1) (cap = 512 MiB). If more than MAX_DECODED_BYTES bytes are written, the function bails with this message. The compressed temp file is removed afterwards.","triggerScenarios":"Calling download_and_decode / download_cli_artifact_from_gcs with an artifact whose decompressed size exceeds 512 MiB (512 * 1024 * 1024 bytes) — either a genuinely oversized binary or corrupt/hostile compressed data that expands far beyond the compressed size.","commonSituations":"A release pipeline accidentally publishes an uncompressed or debug-build artifact; a corrupted/truncated download confuses the decoder into producing garbage bytes; a malicious or compromised mirror serves a decompression bomb; a codec mismatch (decoding a plain file as compressed).","solutions":["Check the artifact size on the release/mirror and publish a correctly compressed binary under 512 MiB decoded.","Re-download the compressed artifact — the source copy may be corrupted; compare its checksum/hash if published.","Ensure the right codec is used for the file extension (do not gunzip a non-gzip artifact).","If you legitimately need larger binaries, raise MAX_DECODED_BYTES in auto_update.rs (currently 512 MiB).","Verify the publisher did not accidentally ship a debug/unstripped build inflating the decoded size."],"exampleFix":"// before: accepting any size, risking disk exhaustion\nlet mut out = std::fs::File::create(&bin_out)?;\nstd::io::copy(&mut decoder, &mut out).context(\"decode\")?;\n\n// after: the library's capped decode (already implemented)\nconst MAX_DECODED_BYTES: u64 = 512 * 1024 * 1024;\nlet mut capped = decoder.take(MAX_DECODED_BYTES + 1);\nlet written = std::io::copy(&mut capped, &mut out).context(\"decode\")?;\nif written > MAX_DECODED_BYTES {\n    anyhow::bail!(\"decoded artifact exceeds the {MAX_DECODED_BYTES}-byte cap\");\n}","handlingStrategy":"validation","validationCode":"// Validate the compressed artifact's plausible expanded size before decoding\nconst MAX_DECODED_BYTES: u64 = 512 * 1024 * 1024;\nlet comp_len = std::fs::metadata(&comp_tmp)?.len();\n// gzip ratio is rarely better than ~1000:1 for real binaries; reject absurd inputs early\nif comp_len == 0 || comp_len > MAX_DECODED_BYTES {\n    anyhow::bail!(\"artifact size {} implausible for decoding\", comp_len);\n}","typeGuard":"fn within_decoded_cap(written: u64, cap: u64) -> bool {\n    written <= cap\n}","tryCatchPattern":"match download_and_decode(&comp_tmp, &bin_out).await {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"byte cap\") => {\n        eprintln!(\"artifact exceeds 512 MiB decode cap; verify you fetched the official release\");\n        std::fs::remove_file(&comp_tmp).ok();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always decode with .take(cap + 1) so runaway decompression is bounded.","Publish stripped, compressed release binaries and keep decoded size well under the cap.","Verify published artifact checksums before decoding.","Clean up temp files on failure (the function already removes comp_tmp)."],"tags":["size-limit","decompression","validation","updater"],"backgroundTag":"size-limit-exceeded","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}