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
- Log bytes.toString('utf8').slice(0, 200) to see what was actually received — it usually reveals an HTML error page or empty body.
- Check the HTTP status code of the manifest fetch before passing bytes to validateManifest.
- Confirm the fetch URL points at the manifest endpoint with the correct Accept header (application/vnd.oci.image.manifest.v1+json).
- 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
- Check HTTP status and Content-Type of the manifest response before parsing.
- Send the correct OCI Accept header on manifest requests.
- Log the first bytes of any non-JSON response to diagnose registry auth/redirect issues.
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
- Cannot read JSON object
- gh returned invalid JSON
- gh returned invalid JSON
- Malformed coordination JSON in body
- Memory frontmatter field in
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)