paperclipai/paperclip · error · Error

Unsupported live campaign schema in ${campaignPath}

Error message

Unsupported live campaign schema in ${campaignPath}

What it means

maintainedRosterSelection loads campaigns/live-direct-full.json from the program root and requires it to declare schema "paperclip-runner/live-campaign/v1" and an array 'lanes'. Any other shape is rejected with the campaign path in the message. This keeps the maintained campaign contract explicit so downstream lane filtering behaves predictably.

Source

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

  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,
        resolve(dirname(campaignPath), String(lane.roster ?? "")),
        "Campaign roster",
      );
      return basename(rosterPath);
    });
  if (selected.length === 0 || new Set(selected).size !== selected.length) {
    throw new Error(
      "Maintained live campaign must contain unique enabled rosters",
    );
  }
  return new Set(selected);
}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Set "schema": "paperclip-runner/live-campaign/v1" at the top level of campaigns/live-direct-full.json.
  2. Ensure the campaign defines lanes as an array of lane objects.
  3. If the schema intentionally changed, update maintainedRosterSelection (and related loaders) to accept the new schema version.

Example fix

// before
{ "schema": "live-campaign", "laneList": [...] }
// after
{ "schema": "paperclip-runner/live-campaign/v1", "lanes": [...] }
Defensive patterns

Strategy: validation

Validate before calling

const campaign = JSON.parse(await readFile(campaignPath, "utf8"));
if (campaign.schema !== "paperclip-runner/live-campaign/v1" || !Array.isArray(campaign.lanes)) throw new Error(`Unsupported campaign schema in ${campaignPath}`);

Type guard

const isLiveCampaignV1 = (c) => c?.schema === "paperclip-runner/live-campaign/v1" && Array.isArray(c.lanes);

Try / catch

try {
  const selection = await maintainedRosterSelection(programRoot);
} catch (err) {
  if (err.message.startsWith("Unsupported live campaign schema")) console.error("Check campaigns/live-direct-full.json schema field and lanes array.");
  throw err;
}

Prevention

When it happens

Trigger: Calling maintainedRosterSelection when the campaign file's schema field is missing, set to another version string, or when campaign.lanes is not an array (absent, an object, or null).

Common situations: Campaign file edited to a newer/renamed schema string without updating the script; lanes field renamed or restructured; stale campaign file from an older layout; file replaced with a differently-shaped export.

Related errors


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