paperclipai/paperclip · error · Error

Roster selection must contain unique comma-separated IDs

Error message

Roster selection must contain unique comma-separated IDs

What it means

parseRosterSelection parses a comma-separated list of roster IDs and requires the list to be non-empty after trimming and free of duplicates. It backs the --rosters style selection flag for the eval campaign. Empty, all-duplicate, or malformed selections are rejected.

Source

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

  }
  if (config.provider === "acpx") {
    if (config.acpxAgent === "pi") return "OPENROUTER_API_KEY";
    if (config.acpxAgent === "claude") return "ANTHROPIC_API_KEY";
    if (config.acpxAgent === "codex") return "OPENAI_API_KEY";
  }
  throw new Error(
    `No credential policy for ${config.provider ?? "codex"}/${config.acpxAgent ?? "default"}`,
  );
}

function parseRosterSelection(value) {
  if (!value?.trim() || value.trim() === "all") return null;
  const selected = value
    .split(",")
    .map((entry) => entry.trim())
    .filter(Boolean);
  if (selected.length === 0 || new Set(selected).size !== selected.length) {
    throw new Error("Roster selection must contain unique comma-separated IDs");
  }
  return new Set(selected);
}

async function maintainedRosterSelection(programRoot) {
  const campaignPath = resolve(programRoot, "campaigns/live-direct-full.json");
  const campaign = await loadObject(campaignPath);
  if (
    campaign.schema !== "paperclip-runner/live-campaign/v1" ||
    !Array.isArray(campaign.lanes)
  ) {
    throw new Error(`Unsupported live campaign schema in ${campaignPath}`);
  }
  const selected = campaign.lanes
    .filter((lane) => lane.executionClass !== "disabled")
    .map((lane) => {
      const rosterPath = inside(
        programRoot,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Remove duplicate IDs from the comma-separated list.
  2. Ensure at least one non-empty ID remains after the commas.
  3. Use 'all' (or omit the flag) to select the maintained roster set instead of listing IDs.

Example fix

// before
node runner-protocol-eval-campaign.mjs --rosters "codex-core,codex-core"
// after
node runner-protocol-eval-campaign.mjs --rosters "codex-core,claude-core"
Defensive patterns

Strategy: validation

Validate before calling

const entries = value.split(",").map((s) => s.trim()).filter(Boolean);
if (entries.length === 0 || new Set(entries).size !== entries.length) throw new Error("Roster selection must be unique, comma-separated IDs");

Type guard

const isValidSelection = (v) => {
  if (typeof v !== "string") return false;
  const entries = v.split(",").map((s) => s.trim()).filter(Boolean);
  return entries.length > 0 && new Set(entries).size === entries.length;
};

Try / catch

try {
  const selection = parseRosterSelection(argv.rosters);
} catch (err) {
  if (err.message.includes("unique comma-separated")) console.error("Pass each roster ID once, e.g. --rosters a,b,c");
  throw err;
}

Prevention

When it happens

Trigger: Passing a selection value like 'r1,r1', 'r1,,r1' (dedupes to unique so fine, but '' entries filtered — duplicates still counted on raw trimmed list), or a value that trims to nothing but isn't empty/'all', e.g. passing ' ' with extra content or 'r1,' with duplicates elsewhere.

Common situations: CLI flag typed as --rosters 'a,a'; scripted invocation building the list from a source with repeated IDs; pasting a list that already contains the same roster twice.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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