{"record":{"id":"11e942ce9561dfd0","repo":"affaan-m/ECC","slug":"retrospective-invalid-inputs","errorCode":"retrospective.invalid_inputs","errorMessage":"supply 1 to ${MAX_INPUTS} capsule directory paths","messagePattern":"supply 1 to (.+?) capsule directory paths","errorType":"error_code","errorClass":"RetrospectiveError","httpStatus":null,"severity":"error","filePath":"scripts/lib/eval-harness/retrospective.js","lineNumber":67,"sourceCode":"    by_effect_class: byEffect,\n    sources: [...projections].sort((a, b) => compare(identity(a), identity(b)))\n      .map(item => ({\n        identity_hash: hashValue([item.run_id, item.capsule_id]),\n        ...Object.fromEntries(SOURCE_FIELDS.map(key => [key, item[key]])),\n      })),\n  };\n}\n\n/**\n * Group up to 100 selected snapshots of one task family by harness_version.\n * Duplicate identities count once only if the verified projections match.\n * Different checkpoints of the same identity are ambiguous and refused.\n * Does not read saved projections, copy payloads, write files or invoke tools.\n */\nfunction groupCapsules(dirs) {\n  if (!Array.isArray(dirs) || dirs.length < 1 || dirs.length > MAX_INPUTS\n      || !Array.from(dirs).every(dir => typeof dir === 'string' && dir.trim() && !dir.includes('\\0'))) {\n    throw new RetrospectiveError('retrospective.invalid_inputs', `supply 1 to ${MAX_INPUTS} capsule directory paths`);\n  }\n  const projections = dirs.map(readProjection);\n  const family = projections[0].task_family;\n  const unique = new Map();\n  for (const [index, projection] of projections.entries()) {\n    if (projection.task_family !== family) {\n      throw new RetrospectiveError('retrospective.mixed_task_families', 'all capsules must have the same task family', index);\n    }\n    const key = identity(projection);\n    const previous = unique.get(key);\n    if (previous && previous.projection_hash !== projection.projection_hash) {\n      throw new RetrospectiveError('retrospective.conflicting_identity', 'conflicting snapshots share a capsule identity', index);\n    }\n    unique.set(key, projection);\n  }\n  const byHarness = new Map();\n  for (const projection of unique.values()) {\n    const version = projection.harness_version;","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/eval-harness/retrospective.js#L49-L85","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","solutions":["Ensure the array contains between 1 and MAX_INPUTS string paths before calling","Check the glob/command that produces the list actually matched capsule directories","Trim and filter the list: dirs.filter(d => typeof d === 'string' && d.trim())","Split oversized batches into multiple groupCapsules invocations"],"exampleFix":"// before\nawait groupCapsules(foundDirs) // foundDirs may be []\n// after\nconst clean = foundDirs.filter(d => typeof d === 'string' && d.trim());\nif (clean.length >= 1 && clean.length <= MAX_INPUTS) await groupCapsules(clean);","handlingStrategy":"validation","validationCode":"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')); }\nif (!validateDirs(dirs)) throw new Error('groupCapsules needs 1..MAX_INPUTS non-empty string paths');","typeGuard":"const isCapsuleDirList = (v) => Array.isArray(v) && v.length >= 1 && v.every((d) => typeof d === 'string' && d.trim().length > 0);","tryCatchPattern":"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; }","preventionTips":["Check glob expansion results before invoking (a glob matching nothing yields [])","Filter/trim the directory list before passing it","Keep batch sizes within MAX_INPUTS and split larger sets","Quote shell arguments to avoid stray bytes in paths"],"tags":["validation","input","retrospective"],"backgroundTag":"invalid-argument-value","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}