affaan-m/ECC · error · Error
has an invalid expected digest.
Error message
${label} has an invalid expected digest. What it means
assertDigest validates that an expected digest string is well-formed before comparing it against the actual SHA-256 of the provided bytes. The library throws this error when the expectedDigest argument does not match SHA256_PATTERN (a full 'sha256:<64 hex chars>' form). It is a pre-check to fail fast on malformed digests before any hashing work.
Solutions
- Print the expectedDigest value and verify it matches /^sha256:[0-9a-f]{64}$/ before passing it in.
- Regenerate the digest with the same helper the library uses: `sha256:${crypto.createHash('sha256').update(bytes).digest('hex')}`.
- If the source stores bare hex, prefix it with 'sha256:' and lowercase it.
- Check whether an upstream tool (skopeo, crane, docker) emits a different format and normalize it on read.
Example fix
// before
assertDigest(bytes, manifest.layers[0].digest.toUpperCase(), 'layer');
// after
const d = manifest.layers[0].digest;
if (!/^sha256:[0-9a-f]{64}$/.test(d)) throw new Error('normalize digest first');
assertDigest(bytes, d, 'layer'); Defensive patterns
Strategy: validation
Validate before calling
function isValidDigest(d) { return typeof d === 'string' && /^sha256:[0-9a-f]{64}$/.test(d); }
if (!isValidDigest(expected)) throw new Error('bad digest format before calling installNasiko'); Type guard
const isSha256Digest = (v) => typeof v === 'string' && /^sha256:[0-9a-f]{64}$/.test(v); Try / catch
try { installNasiko(opts); } catch (e) { if (e.message.includes('invalid expected digest')) { console.error('digest format wrong:', opts.digest); } else throw e; } Prevention
- Store digests exactly as produced by sha256sum with a lowercase hex encoding plus 'sha256:' prefix.
- Add a schema check (zod/regex) at the config boundary where digests are loaded.
- Never hand-edit digest strings; regenerate them from the artifact.
When it happens
Trigger: Calling assertDigest (directly or via installNasiko) with an expectedDigest that is not a valid 'sha256:' prefixed 64-character hex string — e.g. truncated digest, uppercase hex, wrong algorithm prefix, or a raw hex string without the sha256: prefix.
Common situations: A release manifest or CI pipeline stores digests in a different format (bare hex, sha512:, base64), a value was hand-truncated while copying, or an older release artifact schema used a legacy digest format.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- -32602
- a claim token is required
- a confirmed nonempty coordinate is required
- a generated candidate cannot claim original-source identity
- A managed install plan with operations is required.
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/b0861bcc30faef4b.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/nasiko-release.js:55
return { os: osName, arch, binaryName: osName === 'windows' ? 'nasiko.exe' : 'nasiko' };
}
function getQualifiedRelease(version, platform = process.platform, architecture = process.arch) {
if (!/^v\d+\.\d+\.\d+$/.test(String(version || ''))) {
throw new Error('Nasiko installation requires a pinned version such as v0.1.0; latest is not allowed.');
}
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 };View on GitHub (pinned to 8321021c54)