affaan-m/ECC · error · Error
Unsafe Nasiko archive: truncated tar header.
Error message
Unsafe Nasiko archive: truncated tar header.
What it means
While walking the gunzipped tar stream in 512-byte blocks, extractQualifiedTarGzip throws this error when fewer than 512 bytes remain before a header could be read — the tar stream ends mid-header. This is the library's truncation guard: a well-formed tar always ends with two zero blocks, never with a partial header.
Solutions
- Re-download the artifact and retry — a tar ending mid-header almost always means a truncated transfer.
- Compare the artifact's sha256 against the manifest digest; if it matches yet the tar is truncated, the publisher shipped a bad artifact.
- Check the artifact file size on disk versus the Content-Length / declared layer.size from the manifest.
- Clear local caches of the artifact to rule out a partially written cache entry.
Example fix
// before
extractQualifiedTarGzip(fs.readFileSync('artifact.gz'), 'bin'); // truncated tar header
// after
const manifest = JSON.parse(fs.readFileSync('manifest.json', 'utf8'));
const bytes = fs.readFileSync('artifact.gz');
if (bytes.length !== manifest.layers[0].size) {
throw new Error(`truncated download: ${bytes.length} of ${manifest.layers[0].size} bytes — refetch`);
}
assertDigest(bytes, manifest.layers[0].digest, 'artifact');
extractQualifiedTarGzip(bytes, 'bin'); Defensive patterns
Strategy: validation
Validate before calling
if (bytes.length !== declaredLayerSize) throw new Error(`incomplete artifact: ${bytes.length}/${declaredLayerSize} bytes`);
assertDigest(bytes, declaredDigest, 'artifact'); Type guard
null
Try / catch
try { installNasiko(opts); } catch (e) { if (e.message.includes('truncated tar header')) { await refetchArtifact(); return installNasiko(opts); } throw e; } Prevention
- Compare downloaded byte count with the manifest's declared size immediately after fetch.
- Verify sha256 before install to catch truncation with a clearer error.
- Avoid reading artifacts from flaky caches; re-download on any length mismatch.
When it happens
Trigger: The decompressed tar length is not a multiple of 512 such that the final partial block begins a header (offset + 512 > tar.length) — e.g. the gzip stream was truncated before transfer completed, or the file was concatenated/cut.
Common situations: Interrupted downloads that gzip-decompress 'successfully' up to the cut point, storage corruption on a cached artifact, or a publishing pipeline that wrote a partial tar before gzipping.
Related errors
- Nasiko archive is truncated.
- Unsafe Nasiko archive: missing complete tar terminator.
- Unsafe Nasiko archive: expected exactly one bounded regular…
- Unsafe Nasiko archive: incomplete terminator or nonzero…
- Unsafe Nasiko archive: invalid tar size field.
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/8240a1d2cabe9cf6.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/nasiko-release.js:99
const field = block.subarray(offset, offset + length).toString('ascii');
const match = /^ *([0-7]+)[ \0]*$/.exec(field);
if (!match) throw new Error('Unsafe Nasiko archive: invalid tar size field.');
const size = Number.parseInt(match[1], 8);
if (!Number.isSafeInteger(size) || size < 0) {
throw new Error('Unsafe Nasiko archive: invalid tar size field.');
}
return size;
}
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;View on GitHub (pinned to 8321021c54)