affaan-m/ECC · error · Error

Nasiko manifest layer is not a qualified gzip artifact.

Error message

Nasiko manifest layer is not a qualified gzip artifact.

What it means

validateManifest requires the single OCI layer to have mediaType 'application/gzip' and a well-formed sha256 digest. This error is thrown when either check fails, meaning the layer is not the qualified gzip artifact the installer knows how to verify and extract.

Solutions

  1. Inspect layers[0].mediaType and compare against 'application/gzip'; republish the artifact with the required media type if you control the pipeline.
  2. Verify layers[0].digest matches /^sha256:[0-9a-f]{64}$/ and fix the publishing step that emits it.
  3. Confirm you are reading the OCI image manifest, not an index/manifest list whose entry shapes differ.
  4. Use a digest-pinned reference so you get the exact artifact version this library was built for.

Example fix

// before
validateManifest(manifestBytes); // throws: mediaType is tar+gzip
// after
const m = JSON.parse(manifestBytes.toString('utf8'));
const layer = m.layers[0];
if (layer.mediaType !== 'application/gzip') {
  throw new Error(`republish required: got mediaType ${layer.mediaType}`);
}
validateManifest(manifestBytes);
Defensive patterns

Strategy: validation

Validate before calling

const layer = JSON.parse(manifestBytes.toString('utf8')).layers[0];
if (layer.mediaType !== 'application/gzip' || !/^sha256:[0-9a-f]{64}$/.test(layer.digest)) throw new Error('layer not qualified gzip artifact');

Type guard

const isQualifiedGzipLayer = (l) => l?.mediaType === 'application/gzip' && typeof l?.digest === 'string' && /^sha256:[0-9a-f]{64}$/.test(l.digest);

Try / catch

try { installNasiko(opts); } catch (e) { if (e.message.includes('not a qualified gzip artifact')) console.error('check publisher mediaType settings'); throw e; }

Prevention

When it happens

Trigger: The manifest's layers[0].mediaType is something else (e.g. application/vnd.oci.image.layer.v1.tar+gzip, application/vnd.docker...tar, or application/octet-stream), or layers[0].digest fails SHA256_PATTERN (wrong prefix, uppercase, truncated).

Common situations: Publishing pipeline changed the layer compression or media type, artifact was built with docker instead of the OCI toolchain, or digest format drifted between publisher versions.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

function digestBytes(bytes) {
  return `sha256:${crypto.createHash('sha256').update(bytes).digest('hex')}`;
}

function assertDigest(bytes, expectedDigest, label) {
  if (!SHA256_PATTERN.test(expectedDigest)) throw new Error(`${label} has an invalid expected digest.`);
  const actual = digestBytes(bytes);
  if (actual !== expectedDigest) throw new Error(`${label} digest mismatch: expected ${expectedDigest}, got ${actual}.`);
}

function validateManifest(bytes) {
  let manifest;
  try { manifest = JSON.parse(bytes.toString('utf8')); } catch (_error) { throw new Error('Nasiko manifest is not valid JSON.'); }
  if (manifest.schemaVersion !== 2 || !Array.isArray(manifest.layers) || manifest.layers.length !== 1) {
    throw new Error('Nasiko manifest must contain exactly one OCI layer.');
  }
  const layer = manifest.layers[0];
  if (layer.mediaType !== 'application/gzip' || !SHA256_PATTERN.test(layer.digest)) {
    throw new Error('Nasiko manifest layer is not a qualified gzip artifact.');
  }
  if (!Number.isSafeInteger(layer.size) || layer.size <= 0 || layer.size > MAX_ARCHIVE_BYTES) {
    throw new Error('Nasiko manifest layer size is outside the allowed range.');
  }
  return { digest: layer.digest, size: layer.size };
}

function readTarString(block, offset, length) {
  return block.subarray(offset, offset + length).toString('utf8').replace(/\0.*$/, '');
}

function readTarOctal(block, offset, length) {
  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.');

View on GitHub (pinned to 8321021c54)