affaan-m/ECC · error · Error

Unsafe Nasiko archive: incomplete terminator or nonzero…

Error message

Unsafe Nasiko archive: incomplete terminator or nonzero trailing data.

What it means

extractQualifiedTarGzip validates that a gunzipped Nasiko tar archive ends with the required two-block (1024-byte) zero terminator and that no nonzero bytes follow the first terminator block. This guards against truncated or tampered archives. If the terminator is incomplete or trailing data is nonzero, the archive is rejected as unsafe.

Solutions

  1. Re-download the archive from the registry and verify again.
  2. Check that the download completed fully (compare Content-Length / checksum) before extraction.
  3. Ensure the gunzip step produced the complete tar; retry with a fresh connection.
  4. Report the artifact to the registry if the published tarball is corrupted.

Example fix

// before
const tar = gunzipSync(downloadedBuffer); // possibly truncated
cleanup(tar);
// after
const expected = await fetchContentLength(url);
if (downloadedBuffer.length !== expected) throw new Error('Incomplete download; refetch before extracting.');
const tar = gunzipSync(downloadedBuffer);
cleanup(tar);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!downloadedBuffer || downloadedBuffer.length < expectedSize) throw new Error('Incomplete archive before extraction.');

Type guard

const isCompleteTar = (buf) => Buffer.isBuffer(buf) && buf.length >= 1024 && buf.subarray(buf.length - 1024).every(b => b === 0);

Try / catch

try { const bin = await nasiko.downloadAndExtract(url, name); } catch (err) { if (err.message.includes('Unsafe Nasiko archive')) { await refetchAndVerifyChecksum(url); } else { throw err; } }

Prevention

When it happens

Trigger: Calling extractQualifiedTarGzip (or any higher-level download/verify flow that uses it) with an archive whose gzip payload is cut off before 1024 trailing zero bytes, or that has appended bytes after the first 512-byte zero block of the terminator.

Common situations: Partially downloaded tarballs from a flaky network or interrupted fetch; corrupted cache files; archives rebuilt or repacked by tools that append junk; a truncated upload in a mirror.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/3df7fcf993c44e22. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/nasiko-release.js:108

function extractQualifiedTarGzip(archiveBytes, expectedName) {
  let tar;
  try { tar = zlib.gunzipSync(archiveBytes, { maxOutputLength: MAX_BINARY_BYTES + 2048 }); }
  catch (_error) { throw new Error('Nasiko archive is invalid or exceeds the decompressed size limit.'); }
  let offset = 0;
  let binary = null;
  let terminated = false;
  while (offset < tar.length) {
    if (offset + 512 > tar.length) throw new Error('Unsafe Nasiko archive: truncated tar header.');
    const header = tar.subarray(offset, offset + 512);
    if (header.every(byte => byte === 0)) {
      const terminatorEnd = offset + 1024;
      if (
        terminatorEnd > tar.length
        || !tar.subarray(offset + 512, terminatorEnd).every(byte => byte === 0)
        || !tar.subarray(terminatorEnd).every(byte => byte === 0)
      ) {
        throw new Error('Unsafe Nasiko archive: incomplete terminator or nonzero trailing data.');
      }
      terminated = true;
      break;
    }
    const name = readTarString(header, 0, 100);
    const prefix = readTarString(header, 345, 155);
    const type = String.fromCharCode(header[156] || 48);
    const size = readTarOctal(header, 124, 12);
    const start = offset + 512;
    const end = start + size;
    const paddedEnd = start + Math.ceil(size / 512) * 512;
    if (!Number.isSafeInteger(end) || paddedEnd > tar.length) throw new Error('Nasiko archive is truncated.');
    const payload = tar.subarray(start, end);
    if (!tar.subarray(end, paddedEnd).every(byte => byte === 0)) {
      throw new Error('Unsafe Nasiko archive: nonzero tar padding.');
    }
    const isBinary = !prefix && name === expectedName && (type === '0' || type === '\0');
    const isAppleDouble = !prefix && name === `._${expectedName}` && type === '0' && size <= 1024 * 1024;

View on GitHub (pinned to 8321021c54)