affaan-m/ECC · error · RetrospectiveError

retrospective.invalid_inputs

retrospective.invalid_inputs

Error message

supply 1 to ${MAX_INPUTS} capsule directory paths

What it means

groupCapsules validates its input list before doing any work: it must be an array of 1 to MAX_INPUTS non-empty strings with no NUL bytes. Anything else — an empty array, a non-array, too many directories, or entries that are not clean non-empty strings — is rejected with this error before any capsule is read.

Solutions

  1. Ensure the array contains between 1 and MAX_INPUTS string paths before calling
  2. Check the glob/command that produces the list actually matched capsule directories
  3. Trim and filter the list: dirs.filter(d => typeof d === 'string' && d.trim())
  4. Split oversized batches into multiple groupCapsules invocations

Example fix

// before
await groupCapsules(foundDirs) // foundDirs may be []
// after
const clean = foundDirs.filter(d => typeof d === 'string' && d.trim());
if (clean.length >= 1 && clean.length <= MAX_INPUTS) await groupCapsules(clean);
Defensive patterns

Strategy: validation

Validate before calling

function validateDirs(dirs) { return Array.isArray(dirs) && dirs.length >= 1 && dirs.length <= MAX_INPUTS && dirs.every(d => typeof d === 'string' && d.trim() && !d.includes('\0')); }
if (!validateDirs(dirs)) throw new Error('groupCapsules needs 1..MAX_INPUTS non-empty string paths');

Type guard

const isCapsuleDirList = (v) => Array.isArray(v) && v.length >= 1 && v.every((d) => typeof d === 'string' && d.trim().length > 0);

Try / catch

try { groupCapsules(dirs); } catch (e) { if (e.code === 'retrospective.invalid_inputs') console.error('check the glob that produced the dir list; expected 1..MAX_INPUTS clean strings'); throw e; }

Prevention

When it happens

Trigger: Calling groupCapsules([]), groupCapsules('dir'), groupCapsules with more than MAX_INPUTS paths, or an array containing null/undefined/empty-string/whitespace-only entries or paths containing '\0'.

Common situations: Shell globbing that expands to zero files on a fresh checkout; a CLI wrapper passing undefined when no arguments were given; scripted batch runs accumulating more capsule directories than MAX_INPUTS allows; trailing NUL bytes from exec-style argument handling.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/11e942ce9561dfd0. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/eval-harness/retrospective.js:67

    by_effect_class: byEffect,
    sources: [...projections].sort((a, b) => compare(identity(a), identity(b)))
      .map(item => ({
        identity_hash: hashValue([item.run_id, item.capsule_id]),
        ...Object.fromEntries(SOURCE_FIELDS.map(key => [key, item[key]])),
      })),
  };
}

/**
 * Group up to 100 selected snapshots of one task family by harness_version.
 * Duplicate identities count once only if the verified projections match.
 * Different checkpoints of the same identity are ambiguous and refused.
 * Does not read saved projections, copy payloads, write files or invoke tools.
 */
function groupCapsules(dirs) {
  if (!Array.isArray(dirs) || dirs.length < 1 || dirs.length > MAX_INPUTS
      || !Array.from(dirs).every(dir => typeof dir === 'string' && dir.trim() && !dir.includes('\0'))) {
    throw new RetrospectiveError('retrospective.invalid_inputs', `supply 1 to ${MAX_INPUTS} capsule directory paths`);
  }
  const projections = dirs.map(readProjection);
  const family = projections[0].task_family;
  const unique = new Map();
  for (const [index, projection] of projections.entries()) {
    if (projection.task_family !== family) {
      throw new RetrospectiveError('retrospective.mixed_task_families', 'all capsules must have the same task family', index);
    }
    const key = identity(projection);
    const previous = unique.get(key);
    if (previous && previous.projection_hash !== projection.projection_hash) {
      throw new RetrospectiveError('retrospective.conflicting_identity', 'conflicting snapshots share a capsule identity', index);
    }
    unique.set(key, projection);
  }
  const byHarness = new Map();
  for (const projection of unique.values()) {
    const version = projection.harness_version;

View on GitHub (pinned to 8321021c54)