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

  1. Add the required fields: e.g. { "version": 1, "family": "my-task-family", "tasks": [ ... ] }.
  2. Populate tasks with at least one task object.
  3. Validate the JSON parses to an object (`node -e "console.log(typeof require('./taskset.json'))"`).
  4. 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

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


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)