affaan-m/ECC · error · GateError
gate.variant_missing
gate.variant_missing
Error message
variant.json missing in ${resolved} What it means
loadVariant resolves the variant directory, looks for variant.json inside it, and when that file does not exist (ENOENT) throws GateError('gate.variant_missing') including the resolved directory. Every variant capsule must contain a variant.json manifest describing name, effect_class, and entry.
Solutions
- Confirm the directory contains variant.json (`ls <dir>/variant.json`); create or regenerate it if missing.
- Check the path passed to loadVariant points at the variant directory itself, not its parent or the capsule root.
- Regenerate the variant from the capsule source so the manifest is emitted.
- Run the gate from the expected working directory or pass an absolute path.
Example fix
// before
loadVariant('./variants') // parent dir, no manifest inside
// after
loadVariant('./variants/baseline') // contains variant.json Defensive patterns
Strategy: try-catch
Validate before calling
const fs = require('fs'), path = require('path');
function variantManifestExists(dir) {
return fs.existsSync(path.join(path.resolve(dir), 'variant.json'));
}
if (!variantManifestExists(dir)) throw new Error(`variant.json missing in ${path.resolve(dir)}`); Type guard
function isDirWithManifest(p) {
try {
return require('fs').statSync(p).isDirectory() &&
require('fs').statSync(require('path').join(p, 'variant.json')).isFile();
} catch { return false; }
} Try / catch
try {
const variant = loadVariant(dir);
} catch (e) {
if (e.code === 'gate.variant_missing') {
console.error(`${e.message} — regenerate the variant or fix the path`);
} else throw e;
} Prevention
- Always pass the variant directory itself, not its parent.
- Generate variant.json with the capsule tooling; never delete it manually.
- Use absolute paths or run the gate from a known working directory.
- Add a preflight existence check in CI before gate runs.
When it happens
Trigger: Calling loadVariant on a directory without variant.json; passing the wrong directory (e.g. the capsule root or a parent); the manifest was deleted, renamed, or never generated.
Common situations: Typo'd path to the variant dir; a fresh clone where variants were never generated; a script that writes manifest to a different filename (variants.json, manifest.json); running the gate from the wrong working directory with a relative path.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- Failed to save package manager config to
- Failed to save package manager preference
- Install manifests not found under
- memory connector path is not a directory
- agent profile inheritance cycle
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/8dff63dd0874b903.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/eval-harness/gate.js:116
function digestDir(dir) {
const hash = crypto.createHash('sha256');
for (const relative of listFiles(dir)) {
hash.update(relative);
hash.update('\0');
hash.update(readRegularFile(path.join(dir, relative)));
hash.update('\0');
}
return hash.digest('hex');
}
function loadVariant(dir) {
const resolved = fs.realpathSync(path.resolve(dir));
const manifestPath = path.join(resolved, 'variant.json');
let manifestBytes;
try {
manifestBytes = readRegularFile(manifestPath, 'utf8');
} catch (error) {
if (error.code === 'ENOENT') throw new GateError('gate.variant_missing', `variant.json missing in ${resolved}`);
throw error;
}
const manifest = JSON.parse(manifestBytes);
if (typeof manifest.name !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(manifest.name) || !envelope.EFFECT_CLASSES.includes(manifest.effect_class)) {
throw new GateError('gate.variant_invalid', `variant.json in ${resolved} needs name and a valid effect_class`);
}
const entry = manifest.entry === undefined ? 'run.js' : manifest.entry;
if (typeof entry !== 'string' || !entry || path.isAbsolute(entry) || path.win32.isAbsolute(entry) || entry.includes('\\') || entry.split('/').includes('..')) {
throw new GateError('gate.variant_invalid', 'entry must be a relative regular file within the variant');
}
const entryPath = path.resolve(resolved, entry);
const relative = path.relative(resolved, entryPath);
if (!relative || relative.startsWith('..' + path.sep) || path.isAbsolute(relative) || !listFiles(resolved).includes(relative.split(path.sep).join('/')) || !fs.lstatSync(entryPath).isFile()) {
throw new GateError('gate.variant_invalid', 'entry must be covered by the variant digest');
}
return { dir: resolved, name: manifest.name, effect_class: manifest.effect_class, entry: relative, digest: digestDir(resolved) };
}
View on GitHub (pinned to 8321021c54)