{"record":{"id":"d90e45a3a8941371","repo":"affaan-m/ECC","slug":"nasiko-archive-is-invalid-or-exceeds-the-decompres","errorCode":null,"errorMessage":"Nasiko archive is invalid or exceeds the decompressed size limit.","messagePattern":"Nasiko archive is invalid or exceeds the decompressed size limit\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":83,"sourceCode":"  }\n  const layer = manifest.layers[0];\n  if (layer.mediaType !== 'application/gzip' || !SHA256_PATTERN.test(layer.digest)) {\n    throw new Error('Nasiko manifest layer is not a qualified gzip artifact.');\n  }\n  if (!Number.isSafeInteger(layer.size) || layer.size <= 0 || layer.size > MAX_ARCHIVE_BYTES) {\n    throw new Error('Nasiko manifest layer size is outside the allowed range.');\n  }\n  return { digest: layer.digest, size: layer.size };\n}\n\nfunction readTarString(block, offset, length) {\n  return block.subarray(offset, offset + length).toString('utf8').replace(/\\0.*$/, '');\n}\n\nfunction extractQualifiedTarGzip(archiveBytes, expectedName) {\n  let tar;\n  try { tar = zlib.gunzipSync(archiveBytes, { maxOutputLength: MAX_BINARY_BYTES + 2048 }); }\n  catch (_error) { throw new Error('Nasiko archive is invalid or exceeds the decompressed size limit.'); }\n  let offset = 0;\n  let binary = null;\n  while (offset + 512 <= tar.length) {\n    const header = tar.subarray(offset, offset + 512);\n    if (header.every(byte => byte === 0)) break;\n    const name = readTarString(header, 0, 100);\n    const prefix = readTarString(header, 345, 155);\n    const type = String.fromCharCode(header[156] || 48);\n    const rawSize = readTarString(header, 124, 12).trim();\n    const size = Number.parseInt(rawSize || '0', 8);\n    const start = offset + 512;\n    const end = start + size;\n    if (!Number.isSafeInteger(size) || size < 0 || end > tar.length) throw new Error('Nasiko archive is truncated.');\n    const payload = tar.subarray(start, end);\n    const isBinary = !prefix && name === expectedName && (type === '0' || type === '\\0');\n    const isAppleDouble = !prefix && name === `._${expectedName}` && type === '0' && size <= 1024 * 1024;\n    const isPaxMetadata = !prefix && name === `PaxHeader/${expectedName}` && type === 'x' && size <= 64 * 1024\n      && !/(?:^|\\n)(?:path|linkpath)=/i.test(payload.toString('utf8'));","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/affaan-m/ECC/blob/06c5e118c4d3e6c3b7f9445f973a2194c82de193/scripts/lib/nasiko-release.js#L65-L101","documentation":"extractQualifiedTarGzip in scripts/lib/nasiko-release.js inflates the downloaded layer with zlib.gunzipSync and a maxOutputLength cap of MAX_BINARY_BYTES + 2048 (64 MiB + 2 KiB). A gunzip error (bad header, corrupt stream) or output exceeding the cap throws this error. The cap is the decompression-bomb guard: a small gzip payload cannot expand into unbounded memory.","triggerScenarios":"The layer bytes are not a valid gzip stream, or gunzipping them produces more than 64 MiB + 2 KiB. Concretely: registry served non-gzip content for the pinned digest, the response was truncated mid-stream, or the archive intentionally expands past the binary size limit.","commonSituations":"Corrupted downloads on flaky networks; mirrors substituting content; upstream shipping a debug/unstripped binary larger than 64 MiB; crafted compression-bomb payloads.","solutions":["Re-run the install to rule out transient corruption","Manually fetch the layer and try `gunzip -t` on it to confirm the artifact itself is a valid gzip stream","If the legitimate binary now exceeds 64 MiB, raise MAX_BINARY_BYTES deliberately after review","Report reproducible corrupt-for-pinned-digest artifacts upstream"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await installNasiko({ version: 'v0.1.0' });\n} catch (error) {\n  if (/invalid or exceeds the decompressed size limit/.test(String(error.message))) {\n    // Retry once for transient corruption; if reproducible, gunzip -t the\n    // downloaded layer to decide between oversized binary (raise cap after\n    // review) and corrupt artifact (report upstream).\n  }\n  throw error;\n}","preventionTips":["Keep released binaries under the 64 MiB cap (strip debug symbols before packaging)","Test `gunzip -t` on the produced layer in the release pipeline","Do not raise maxOutputLength casually - the cap is the decompression-bomb guard"],"tags":["gzip","archive","decompression","limits"],"backgroundTag":"gzip-decompression-failed","analyzedSha":"06c5e118c4d3e6c3b7f9445f973a2194c82de193","analyzedAt":"2026-08-18T11:27:13.915Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}