paperclipai/paperclip · error · Error

Unsupported live roster schema in ${rosterFile}

Error message

Unsupported live roster schema in ${rosterFile}

What it means

While iterating roster files, buildProtocolEvalCatalog loads each roster and requires its schema field to equal "paperclip-runner/live-roster/v1". Any other value (or a missing schema) is rejected with the offending roster filename. This guarantees every selected roster follows the current live-roster contract before cases are processed.

Source

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

  const rosterFiles = (await readdir(rosterRoot, { withFileTypes: true }))
    .filter(
      (entry) =>
        entry.isFile() &&
        entry.name.startsWith("live-") &&
        entry.name.endsWith(".json"),
    )
    .map((entry) => entry.name)
    .sort();
  const rosters = [];
  for (const rosterFile of rosterFiles) {
    const rosterPath = resolve(rosterRoot, rosterFile);
    const roster = await loadObject(rosterPath);
    const rosterId = safeId(roster.id, "roster ID");
    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,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Add or correct "schema": "paperclip-runner/live-roster/v1" at the top level of the named roster file.
  2. Remove or move stale rosters that follow an older contract out of the rosters/ directory.
  3. If the roster schema was intentionally versioned up, update the check (and any schema-dependent parsing) in buildProtocolEvalCatalog.

Example fix

// before
{ "id": "codex-core", "cases": [...] }
// after
{ "schema": "paperclip-runner/live-roster/v1", "id": "codex-core", "cases": [...] }
Defensive patterns

Strategy: validation

Validate before calling

const roster = JSON.parse(await readFile(rosterPath, "utf8"));
if (roster.schema !== "paperclip-runner/live-roster/v1") throw new Error(`Unsupported roster schema in ${rosterPath}`);

Type guard

const isLiveRosterV1 = (r) => r?.schema === "paperclip-runner/live-roster/v1" && Array.isArray(r.cases) && r.cases.length > 0;

Try / catch

try {
  const catalog = await buildProtocolEvalCatalog(opts);
} catch (err) {
  if (err.message.startsWith("Unsupported live roster schema")) console.error(`Fix the schema field in the roster named in the error.`);
  throw err;
}

Prevention

When it happens

Trigger: A JSON file in the rosters directory has schema set to another string, an older version like 'live-roster', or lacks the schema field entirely while being selected by the roster selection.

Common situations: Old roster files left in the rosters/ directory from a previous contract; a hand-written roster missing the schema header; copying a campaign-shaped JSON into the rosters directory; renamed schema after a version bump.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/a316e5c2c218de8f. Report an issue: GitHub.