{"record":{"id":"a1b1938e087902c8","repo":"affaan-m/ECC","slug":"nasiko-manifest-must-contain-exactly-one-oci-layer","errorCode":null,"errorMessage":"Nasiko manifest must contain exactly one OCI layer.","messagePattern":"Nasiko manifest must contain exactly one OCI layer\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":64,"sourceCode":"  if (!qualification) throw new Error(`Nasiko ${version} is not qualified for ${normalized.os}/${normalized.arch}.`);\n  return { version, ...normalized, ...qualification, license: LICENSE, sourceUrl: SOURCE_URL };\n}\n\nfunction digestBytes(bytes) {\n  return `sha256:${crypto.createHash('sha256').update(bytes).digest('hex')}`;\n}\n\nfunction assertDigest(bytes, expectedDigest, label) {\n  if (!SHA256_PATTERN.test(expectedDigest)) throw new Error(`${label} has an invalid expected digest.`);\n  const actual = digestBytes(bytes);\n  if (actual !== expectedDigest) throw new Error(`${label} digest mismatch: expected ${expectedDigest}, got ${actual}.`);\n}\n\nfunction validateManifest(bytes) {\n  let manifest;\n  try { manifest = JSON.parse(bytes.toString('utf8')); } catch (_error) { throw new Error('Nasiko manifest is not valid JSON.'); }\n  if (manifest.schemaVersion !== 2 || !Array.isArray(manifest.layers) || manifest.layers.length !== 1) {\n    throw new Error('Nasiko manifest must contain exactly one OCI layer.');\n  }\n  const layer = manifest.layers[0];\n  if (layer.mediaType !== 'application/gzip' || !SHA256_PATTERN.test(layer.digest)) {\n    throw new Error('Nasiko manifest layer is not a qualified gzip artifact.');\n  }\n  if (!Number.isSafeInteger(layer.size) || layer.size <= 0 || layer.size > MAX_ARCHIVE_BYTES) {\n    throw new Error('Nasiko manifest layer size is outside the allowed range.');\n  }\n  return { digest: layer.digest, size: layer.size };\n}\n\nfunction readTarString(block, offset, length) {\n  return block.subarray(offset, offset + length).toString('utf8').replace(/\\0.*$/, '');\n}\n\nfunction readTarOctal(block, offset, length) {\n  const field = block.subarray(offset, offset + length).toString('ascii');\n  const match = /^ *([0-7]+)[ \\0]*$/.exec(field);","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/nasiko-release.js#L46-L82","documentation":"validateManifest enforces that the manifest declares schemaVersion 2 and contains a layers array with exactly one entry, matching this library's single-layer release artifact contract. The error is thrown when any of those structural conditions fail. It protects installNasiko from multi-layer or malformed manifests it cannot verify.","triggerScenarios":"Passing a manifest with schemaVersion other than 2, a missing/non-array layers field, an empty layers array, or a multi-layer image manifest (e.g. a standard multi-arch index or an image with several layers) to validateManifest.","commonSituations":"Pointing the installer at a general-purpose container image instead of the purpose-built Nasiko artifact, a publisher restructured the release to multiple layers, or an old manifest schema (v1) is still cached.","solutions":["Verify you resolved the correct release artifact, not a generic container image with multiple layers.","Check manifest.schemaVersion is 2 and console.log(manifest.layers.length) to see what was actually published.","Refresh the manifest reference (digest-pinned tag) to pick up the current published artifact.","If you must install from a multi-layer image, select and verify the intended layer manually instead of using this library path."],"exampleFix":"// before\nvalidateManifest(fs.readFileSync('image-manifest.json'));\n// after\nconst manifest = JSON.parse(fs.readFileSync('image-manifest.json', 'utf8'));\nif (manifest.schemaVersion !== 2 || manifest.layers?.length !== 1) {\n  throw new Error(`not a Nasiko release manifest: schema=${manifest.schemaVersion}, layers=${manifest.layers?.length}`);\n}\nvalidateManifest(fs.readFileSync('image-manifest.json'));","handlingStrategy":"validation","validationCode":"const m = JSON.parse(manifestBytes.toString('utf8'));\nif (m.schemaVersion !== 2 || !Array.isArray(m.layers) || m.layers.length !== 1) throw new Error('not a single-layer OCI v2 manifest');","typeGuard":"const isSingleLayerManifest = (m) => m?.schemaVersion === 2 && Array.isArray(m.layers) && m.layers.length === 1;","tryCatchPattern":"try { installNasiko(opts); } catch (e) { if (e.message.includes('exactly one OCI layer')) console.error('wrong artifact selected; need the single-layer Nasiko release'); throw e; }","preventionTips":["Digest-pin the release reference so you always resolve the intended artifact.","Validate manifest shape right after fetch, before install.","Keep release publishing to exactly one layer per this library's contract."],"tags":["schema","oci","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}