affaan-m/ECC · error · Error

Nasiko archive is truncated.

Error message

Nasiko archive is truncated.

What it means

extractQualifiedTarGzip computes each tar entry's end and padded end from its size field. If the padded end extends past the decompressed archive length (or the computed end is not a safe integer), the entry payload is incomplete and the archive is rejected as truncated. This prevents reading partial file payloads from malformed tars.

Solutions

  1. Re-download the Nasiko archive and verify its checksum before extraction.
  2. Retry the gunzip/extract step on a fresh buffer to rule out a truncated read.
  3. Check available disk space if the archive was written to disk first.
  4. If intentional, repack the archive correctly with tar so all entries are complete.

Example fix

// before
const tar = extractQualifiedTarGzip(partialBuffer);
// after
const ok = await verifyChecksum(url, sha256);
if (!ok) throw new Error('Archive checksum mismatch; refusing to extract.');
const tar = extractQualifiedTarGzip(await downloadFully(url));
Defensive patterns

Strategy: validation

Validate before calling

if (Buffer.byteLength(tarBuffer) < 512) throw new Error('Tar too small to be valid.');

Try / catch

try { return extractQualifiedTarGzip(tar); } catch (err) { if (err.message === 'Nasiko archive is truncated.') return refetchArchive(url); throw err; }

Prevention

When it happens

Trigger: Calling extractQualifiedTarGzip with a tar whose bytes end in the middle of a file entry: header size field implies paddedEnd > tar.length, or start+size overflows Number.MAX_SAFE_INTEGER.

Common situations: Interrupted download of the release tarball; corrupted gzip decompression; extremely large/corrupted size field in a malicious archive; disk-space truncation when the file was cached.

Related errors


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

Appendix: source

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

      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;
    const isPaxMetadata = !prefix && name === `PaxHeader/${expectedName}` && type === 'x' && size <= 64 * 1024
      && !/(?:^|\n)(?:path|linkpath)=/i.test(payload.toString('utf8'));
    if (isBinary && !binary && size > 0 && size <= MAX_BINARY_BYTES) binary = Buffer.from(payload);
    else if (!isAppleDouble && !isPaxMetadata) throw new Error('Unsafe Nasiko archive: expected exactly one bounded regular binary file.');
    offset = paddedEnd;
  }
  if (!terminated) throw new Error('Unsafe Nasiko archive: missing complete tar terminator.');
  if (!binary) throw new Error('Unsafe Nasiko archive: expected exactly one bounded regular binary file.');
  return binary;
}

function fetchBytes(url, options = {}) {

View on GitHub (pinned to 8321021c54)