affaan-m/ECC · error · GateError
gate.taskset_invalid
gate.taskset_invalid
Error message
taskset needs version, family, and a non-empty tasks array
What it means
loadTaskset parses the taskset JSON and requires it to be an object with truthy `version`, `family`, and a non-empty `tasks` array. Missing or malformed top-level structure throws GateError('gate.taskset_invalid') with this message.
Solutions
- Add the required fields: e.g. { "version": 1, "family": "my-task-family", "tasks": [ ... ] }.
- Populate tasks with at least one task object.
- Validate the JSON parses to an object (`node -e "console.log(typeof require('./taskset.json'))"`).
- Confirm you are loading the actual taskset file, not another JSON artifact.
Example fix
// before (taskset.json)
{ "version": 1, "tasks": [] }
// after
{ "version": 1, "family": "capsule-group", "tasks": [{ "id": "t1", "input": "x", "expected": "y" }] } Defensive patterns
Strategy: validation
Validate before calling
function tasksetStructureOk(ts) {
return ts != null && typeof ts === 'object' &&
Boolean(ts.version) && Boolean(ts.family) &&
Array.isArray(ts.tasks) && ts.tasks.length > 0;
}
const ts = JSON.parse(fs.readFileSync(p, 'utf8'));
if (!tasksetStructureOk(ts)) throw new Error('taskset needs version, family, non-empty tasks'); Type guard
function isTaskset(v) {
return typeof v === 'object' && v !== null &&
'version' in v && 'family' in v && Array.isArray(v.tasks);
} Try / catch
try {
const taskset = loadTaskset(p);
} catch (e) {
if (e.code === 'gate.taskset_invalid' && /version, family/.test(e.message)) {
console.error('Add version, family, and at least one task to the taskset JSON');
} else throw e;
} Prevention
- Use a JSON schema check for {version, family, tasks[]} in CI.
- Never commit empty placeholder tasksets.
- Confirm you are loading the taskset file, not another JSON artifact.
- Include at least one task before registering the taskset.
When it happens
Trigger: JSON file that parses to null/an array/a string; taskset lacking version or family; `tasks` absent, not an array, or an empty array; malformed JSON would throw SyntaxError before this check but structural gaps hit this error.
Common situations: Empty `[]` placeholder tasksets; exports missing metadata fields; a file accidentally saved with `{}`; tasksets authored against an older schema where family was named differently; feeding a capsule metadata file to loadTaskset by mistake.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- application request must be a JSON object
- bundle values must be finite JSON data
- candidate configuration exceeds 1 MiB
- duplicate JSON field
- input_artifacts must be a list of local paths
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/1f25699e5b36eb5c.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/eval-harness/gate.js:139
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) };
}
function loadTaskset(tasksetPath) {
const resolved = path.resolve(tasksetPath);
const taskset = JSON.parse(fs.readFileSync(resolved, 'utf8'));
if (!taskset || typeof taskset !== 'object' || !taskset.version || !taskset.family || !Array.isArray(taskset.tasks) || taskset.tasks.length === 0) {
throw new GateError('gate.taskset_invalid', 'taskset needs version, family, and a non-empty tasks array');
}
if (new Set(taskset.tasks.map(task => task && task.id)).size !== taskset.tasks.length) throw new GateError('gate.taskset_invalid', 'task ids must be unique');
for (const task of taskset.tasks) {
if (!task || typeof task !== 'object' || typeof task.id !== 'string' || !task.id || !('input' in task) || !('expected' in task)) {
throw new GateError('gate.taskset_invalid', 'every task needs id, input, and expected');
}
}
return { ...taskset, path: resolved, digest: sha256Hex(fs.readFileSync(resolved)) };
}
/** Scan variant sources for tripwire patterns and effect-class expansion. */
function scanTripwires(variant, options = {}) {
const rules = options.tripwires || DEFAULT_TRIPWIRES;
const maxRank = envelope.effectRank(options.max_effect_class || 'SE1');
const hits = [];
if (envelope.effectRank(variant.effect_class) > maxRank) {
hits.push({ variant: variant.name, rule: 'effect_class_expansion', file: 'variant.json', line: 1, detail: `${variant.effect_class} exceeds ${options.max_effect_class || 'SE1'}` });
}View on GitHub (pinned to 8321021c54)