affaan-m/ECC · error · Error

Nasiko manifest is not valid JSON.

Error message

Nasiko manifest is not valid JSON.

What it means

validateManifest parses the OCI manifest bytes as UTF-8 JSON and throws this error when JSON.parse fails. The library requires a syntactically valid JSON manifest before it inspects schemaVersion and layers. This guard exists so malformed responses are reported clearly rather than as opaque parse crashes downstream.

Solutions

  1. Log bytes.toString('utf8').slice(0, 200) to see what was actually received — it usually reveals an HTML error page or empty body.
  2. Check the HTTP status code of the manifest fetch before passing bytes to validateManifest.
  3. Confirm the fetch URL points at the manifest endpoint with the correct Accept header (application/vnd.oci.image.manifest.v1+json).
  4. Ensure the response was not left gzip-encoded (Content-Encoding) — decode before validating.

Example fix

// before
validateManifest(await res.arrayBuffer());
// after
const res = await fetch(manifestUrl, { headers: { Accept: 'application/vnd.oci.image.manifest.v1+json' } });
if (!res.ok) throw new Error(`manifest fetch failed: HTTP ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
JSON.parse(bytes.toString('utf8')); // fail with real parse error if still invalid
validateManifest(bytes);
Defensive patterns

Strategy: try-catch

Validate before calling

const text = bytes.toString('utf8').trim();
if (!text.startsWith('{')) throw new Error(`manifest not JSON, starts with: ${text.slice(0, 80)}`);
JSON.parse(text);

Type guard

const looksLikeJson = (b) => b.toString('utf8').trimStart().startsWith('{');

Try / catch

try { installNasiko(opts); } catch (e) { if (e.message.includes('not valid JSON')) console.error('registry returned non-JSON; check URL/status/accept header'); throw e; }

Prevention

When it happens

Trigger: validateManifest is called with bytes that are not valid JSON — e.g. an HTML error page from the registry, empty body, gzip bytes mistakenly passed instead of the decoded manifest, or truncated response.

Common situations: Registry returns 404/403 HTML instead of the manifest, an auth-redirect page is captured, the wrong URL is fetched (tag vs blob), or a network proxy injects content.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

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

  const normalized = normalizePlatform(platform, architecture);
  const qualification = QUALIFIED_RELEASES[version]?.[`${normalized.os}/${normalized.arch}`];
  if (!qualification) throw new Error(`Nasiko ${version} is not qualified for ${normalized.os}/${normalized.arch}.`);
  return { version, ...normalized, ...qualification, license: LICENSE, sourceUrl: SOURCE_URL };
}

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) {

View on GitHub (pinned to 8321021c54)