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
- Re-download the archive from the registry and verify again.
- Check that the download completed fully (compare Content-Length / checksum) before extraction.
- Ensure the gunzip step produced the complete tar; retry with a fresh connection.
- 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
- Always verify the published checksum/size of the download before extraction
- Use resumable, verified downloads (Range + Content-Length) for large artifacts
- Never append data to tarballs after packing
- Cache-evict any archive whose byte length does not match the manifest
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
- Nasiko archive is truncated.
- Unsafe Nasiko archive: expected exactly one bounded regular…
- Unsafe Nasiko archive: missing complete tar terminator.
- Unsafe Nasiko archive: nonzero tar padding.
- application bundle differs from its bound evidence
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)