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

  1. Re-download the artifact and retry — a tar ending mid-header almost always means a truncated transfer.
  2. Compare the artifact's sha256 against the manifest digest; if it matches yet the tar is truncated, the publisher shipped a bad artifact.
  3. Check the artifact file size on disk versus the Content-Length / declared layer.size from the manifest.
  4. 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

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


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)