{"record":{"id":"b31b9987e09ab982","repo":"affaan-m/ECC","slug":"nasiko-archive-size-mismatch","errorCode":null,"errorMessage":"Nasiko archive size mismatch.","messagePattern":"Nasiko archive size mismatch\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":405,"sourceCode":"  if (!options.yes) throw new Error('Nasiko installation requires explicit --yes consent.');\n  fs.mkdirSync(installDirectory, { recursive: true, mode: 0o755 });\n  assertPrivateInstallDirectory(installDirectory);\n  const releaseLock = acquireLifecycleLock(installDirectory);\n  const metadataPath = metadataPathFor(destination);\n  let destinationOwned = false;\n  let metadataOwned = false;\n  try {\n    const existing = inspectInstalledNasiko(destination);\n    if (existing.installed) {\n      if (existing.qualified && existing.version === version) return { ...plan, dryRun: false, installed: true, reused: true };\n      throw new Error('An unqualified or incompatible Nasiko executable or receipt already exists at the destination.');\n    }\n    const retrieve = dependencies.fetchBytes || fetchBytes;\n    const manifestBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/manifests/${release.manifestDigest}`, { accept: 'application/vnd.oci.image.manifest.v1+json', maxBytes: MAX_MANIFEST_BYTES });\n    assertDigest(manifestBytes, release.manifestDigest, 'Nasiko manifest');\n    const layer = validateManifest(manifestBytes);\n    const archiveBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/blobs/${layer.digest}`, { maxBytes: MAX_ARCHIVE_BYTES });\n    if (archiveBytes.length !== layer.size) throw new Error('Nasiko archive size mismatch.');\n    assertDigest(archiveBytes, layer.digest, 'Nasiko archive');\n    const binary = (dependencies.extractBinary || extractQualifiedTarGzip)(archiveBytes, release.binaryName);\n    assertDigest(binary, release.binaryDigest, 'Nasiko binary');\n    if (dependencies.beforePublish) dependencies.beforePublish(destination);\n    const descriptor = fs.openSync(destination, 'wx', 0o700);\n    destinationOwned = true;\n    try {\n      fs.writeFileSync(descriptor, binary);\n      fs.fsyncSync(descriptor);\n      if (fs.fstatSync(descriptor).size !== binary.length) throw new Error('Published Nasiko binary size mismatch.');\n    } finally { fs.closeSync(descriptor); }\n    const metadata = { version, platform: release.os, architecture: release.arch, manifestDigest: release.manifestDigest, artifactDigest: layer.digest, binaryDigest: release.binaryDigest, installedPath: destination, license: release.license, sourceUrl: release.sourceUrl };\n    (dependencies.writeMetadata || writeMetadataExclusive)(metadataPath, metadata);\n    metadataOwned = true;\n    return { ...plan, dryRun: false, installed: true, reused: false, artifactDigest: layer.digest };\n  } catch (error) {\n    if (metadataOwned) fs.rmSync(metadataPath, { force: true });\n    if (destinationOwned) fs.rmSync(destination, { force: true });","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/nasiko-release.js#L387-L423","documentation":"Thrown after downloading the Nasiko tar.gz archive when the byte length of the downloaded blob does not equal the layer size declared in the OCI image manifest. This is an integrity gate before digest verification, ensuring truncated or padded downloads never reach extraction.","triggerScenarios":"fetchBytes (or dependencies.fetchBytes) returns a buffer whose .length differs from validateManifest(manifestBytes).layer.size — e.g. a truncated response, a proxy or registry serving a different/rewritten blob, or a manifest from the registry that disagrees with the actual blob size.","commonSituations":"Flaky network or interrupted download behind a misbehaving proxy; registry storage corruption; CDN/blob middleware that modifies payloads; pointing at a mirror whose manifests and blobs are out of sync.","solutions":["Retry the download; transient truncation is the most common cause.","Verify the registry origin and mirror are consistent — compare manifest layer size against the blob endpoint directly.","Clear any proxy/cache layer between the client and REGISTRY_ORIGIN and retry.","Check MAX_ARCHIVE_BYTES handling: ensure the fetch implementation doesn't silently truncate at the maxBytes limit instead of erroring."],"exampleFix":"// before\nconst archiveBytes = await retrieve(url, { maxBytes: MAX_ARCHIVE_BYTES });\n// after (retry truncated downloads)\nlet archiveBytes;\nfor (let i = 0; i < 3; i++) {\n  archiveBytes = await retrieve(url, { maxBytes: MAX_ARCHIVE_BYTES });\n  if (archiveBytes.length === layer.size) break;\n}","handlingStrategy":"retry","validationCode":"const res = await fetch(blobUrl);\nconst buf = Buffer.from(await res.arrayBuffer());\nif (buf.length !== expectedLayerSize) throw new Error('retry: truncated blob download');","typeGuard":"function isCompleteDownload(bytes, expectedSize) {\n  return bytes instanceof Uint8Array && bytes.length === expectedSize;\n}","tryCatchPattern":"try {\n  await installNasiko({ version });\n} catch (e) {\n  if (e.message === 'Nasiko archive size mismatch.') {\n    await new Promise(r => setTimeout(r, 1000));\n    await installNasiko({ version }); // retry transient truncation\n  } else throw e;\n}","preventionTips":["Retry downloads on flaky networks","Bypass suspicious proxies/caches when integrity checks fail","Verify registry mirror sync health","Cap fetch buffers properly so truncation is explicit, not silent"],"tags":["integrity","download","size-mismatch"],"backgroundTag":"checksum-mismatch","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}