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
- Add or correct "schema": "paperclip-runner/live-roster/v1" at the top level of the named roster file.
- Remove or move stale rosters that follow an older contract out of the rosters/ directory.
- 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
- Template new rosters from an existing v1 roster so the schema header is never omitted.
- Validate all files in rosters/ against live-roster/v1 in CI.
- Archive old-contract rosters outside the rosters/ directory so they are never scanned.
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
- Worktree seed manifest is missing source path diagnostics.
- unsupported request schema
- Public attempt must contain the read-only public chat projec
- Unknown public chat payload field
- Unknown public chat projection field
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/a316e5c2c218de8f.
Report an issue: GitHub.