paperclipai/paperclip · error · Error

Live roster ${rosterId} repeats a case

Error message

Live roster ${rosterId} repeats a case

What it means

buildProtocolEvalCatalog maps each roster.cases entry through safeId and then checks whether the resulting list contains duplicates by comparing Set size to array length. Case IDs must be unique within a roster because each becomes a distinct eval cell keyed by '<rosterId>--<caseId>'; a duplicate would silently overwrite or double-run a cell.

Source

Thrown at packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs:164

    if (selected && !selected.has(rosterId) && !selected.has(rosterFile)) {
      continue;
    }
    if (roster.schema !== "paperclip-runner/live-roster/v1") {
      throw new Error(`Unsupported live roster schema in ${rosterFile}`);
    }
    if (!Array.isArray(roster.cases) || roster.cases.length === 0) {
      throw new Error(`Live roster ${rosterId} has no cases`);
    }
    const configPath = inside(
      programRoot,
      resolve(rosterRoot, String(roster.config ?? "")),
      `Config for ${rosterId}`,
    );
    const config = await loadObject(configPath);
    const credentialName = credentialForConfig(config);
    const cases = roster.cases.map((caseId) => safeId(caseId, "case ID"));
    if (new Set(cases).size !== cases.length) {
      throw new Error(`Live roster ${rosterId} repeats a case`);
    }
    rosters.push({
      rosterId,
      rosterFile,
      configFile: relative(programRoot, configPath).split(sep).join("/"),
      model: String(roster.model ?? config.model ?? "unknown"),
      provider: String(config.provider ?? "codex"),
      driver: String(config.driver ?? "codex_app_server"),
      credentialName,
      cases,
    });
  }
  if (selected) {
    const found = new Set(
      rosters.flatMap((roster) => [roster.rosterId, roster.rosterFile]),
    );
    const missing = [...selected].filter((entry) => !found.has(entry));
    if (missing.length > 0) {

View on GitHub (pinned to 01ad858492)

Solutions

  1. Open the roster file and delete the duplicated case ID from 'cases'.
  2. Deduplicate programmatically before running: [...new Set(roster.cases)].
  3. If two distinct scenarios share a name, rename one case ID so each is unique.

Example fix

// before
"cases": ["case-001", "case-002", "case-001"]
// after
"cases": ["case-001", "case-002"]
Defensive patterns

Strategy: validation

Validate before calling

const ids = roster.cases.map(String);
if (new Set(ids).size !== ids.length) {
  throw new Error(`${rosterFile}: duplicate case IDs`);
}

Type guard

function casesAreUnique(r) {
  return Array.isArray(r.cases) && new Set(r.cases).size === r.cases.length;
}

Prevention

When it happens

Trigger: A live roster file's 'cases' array contains the same case ID string twice (e.g. ["case-001", "case-001"]).

Common situations: Merging roster files or case lists from two branches without deduplicating; copy-paste adding the same case twice; programmatic generation appending a case that is already listed.

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 paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/37a01ddbc2376bf5. Report an issue: GitHub.