affaan-m/ECC · error
Published Nasiko binary size mismatch.
Error message
Published Nasiko binary size mismatch.
What it means
Thrown after writing the Nasiko binary to disk (exclusive 'wx' open at 0700) when the file size reported by fs.fstatSync differs from the in-memory binary buffer length. It confirms the fsync'd file on disk matches what was verified in memory before the install is reported successful.
Solutions
- Check free disk space on the target filesystem; free space if full and retry the install.
- Ensure no concurrent process writes the same destination; the 'wx' open prevents concurrent creates but not post-open interference, so serialize installs.
- Retry the install — transient I/O errors can cause short writes.
- If testing with dependency-injected fs, fix the mock so writeFileSync/fstatSync report consistent sizes.
Example fix
// before
fs.writeFileSync(descriptor, binary);
fs.fsyncSync(descriptor);
// after (check write return + space)
const written = fs.writeFileSync(descriptor, binary);
fs.fsyncSync(descriptor);
if (fs.statSync(destination).size !== binary.length) throw new Error('short write — check disk space'); Defensive patterns
Strategy: try-catch
Validate before calling
import fs from 'node:fs';
import { statfsSync } from 'node:fs';
const space = statfsSync(path.dirname(destination));
if (space.bavail * space.bsize < binaryLength) throw new Error('insufficient disk space'); Type guard
null
Try / catch
try {
await installNasiko({ version });
} catch (e) {
if (e.message === 'Published Nasiko binary size mismatch.') {
// free disk space / stop concurrent writers, then retry
await installNasiko({ version });
} else throw e;
} Prevention
- Monitor free disk space on the install filesystem
- Serialize installs to avoid concurrent writes to the same destination
- Avoid partial fs mocks in tests that skew fstat results
- Retry installs after transient I/O errors
When it happens
Trigger: fs.writeFileSync(descriptor, binary) plus fsync produced a file whose stat size !== binary.length — typically caused by concurrent writers to the same path, a full disk, a custom fs mock in dependencies, or filesystem corruption.
Common situations: Disk-full conditions during install; another process racing to write the same destination path; running under a virtualized/partial fs shim in tests that doesn't persist all bytes.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- artifact byte count mismatch
- destination is not writable by the current user: . Fix the…
- reference source mutated while creating stable snapshot
- Refusing to hash changed install destination
- A normalized install request is required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/c37d3fa0262e3fa4.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/nasiko-release.js:415
if (existing.qualified && existing.version === version) return { ...plan, dryRun: false, installed: true, reused: true };
throw new Error('An unqualified or incompatible Nasiko executable or receipt already exists at the destination.');
}
const retrieve = dependencies.fetchBytes || fetchBytes;
const manifestBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/manifests/${release.manifestDigest}`, { accept: 'application/vnd.oci.image.manifest.v1+json', maxBytes: MAX_MANIFEST_BYTES });
assertDigest(manifestBytes, release.manifestDigest, 'Nasiko manifest');
const layer = validateManifest(manifestBytes);
const archiveBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/blobs/${layer.digest}`, { maxBytes: MAX_ARCHIVE_BYTES });
if (archiveBytes.length !== layer.size) throw new Error('Nasiko archive size mismatch.');
assertDigest(archiveBytes, layer.digest, 'Nasiko archive');
const binary = (dependencies.extractBinary || extractQualifiedTarGzip)(archiveBytes, release.binaryName);
assertDigest(binary, release.binaryDigest, 'Nasiko binary');
if (dependencies.beforePublish) dependencies.beforePublish(destination);
const descriptor = fs.openSync(destination, 'wx', 0o700);
destinationOwned = true;
try {
fs.writeFileSync(descriptor, binary);
fs.fsyncSync(descriptor);
if (fs.fstatSync(descriptor).size !== binary.length) throw new Error('Published Nasiko binary size mismatch.');
} finally { fs.closeSync(descriptor); }
const metadata = { version, platform: release.os, architecture: release.arch, manifestDigest: release.manifestDigest, artifactDigest: layer.digest, binaryDigest: release.binaryDigest, installedPath: destination, license: release.license, sourceUrl: release.sourceUrl };
(dependencies.writeMetadata || writeMetadataExclusive)(metadataPath, metadata);
metadataOwned = true;
return { ...plan, dryRun: false, installed: true, reused: false, artifactDigest: layer.digest };
} catch (error) {
if (metadataOwned) fs.rmSync(metadataPath, { force: true });
if (destinationOwned) fs.rmSync(destination, { force: true });
throw error;
} finally { releaseLock(); }
}
function uninstallNasiko(options = {}, dependencies = {}) {
const version = options.version || 'v0.1.0';
const release = getQualifiedRelease(version, dependencies.platform || process.platform, dependencies.arch || process.arch);
const installDirectory = validateInstallDirectory(options.installDir || defaultInstallDirectory(release, dependencies.environment || process.env, dependencies.homeDirectory || os.homedir()));
const destination = path.join(installDirectory, release.binaryName);
const plan = { dryRun: Boolean(options.dryRun), version, destination };View on GitHub (pinned to 8321021c54)