paperclipai/paperclip · error · Error
Expected an original Actions campaign
Error message
Expected an original Actions campaign
What it means
After loading sourceRoot/campaign.json, refreshProtocolEvalReport asserts the campaignId matches /^gha-[1-9][0-9]*-[1-9][0-9]*$/ — i.e. it must be an original GitHub Actions-generated campaign, not a hand-made or re-published one. The error means campaign.json describes a campaign the refresher refuses to operate on.
Source
Thrown at packages/paperclip-runner/scripts/refresh-runner-protocol-eval-report.mjs:28
export async function refreshProtocolEvalReport({
sourceRoot,
evalsRoot,
viewerRoot,
outputRoot,
revision,
selection,
renderedAt = new Date().toISOString(),
}) {
if (!/^[a-z0-9][a-z0-9-]{0,39}$/.test(revision ?? ""))
throw new Error("A safe, unique --revision is required");
if (await lstat(outputRoot).catch(() => null))
throw new Error("Report refresh output must be a new directory");
const campaign = JSON.parse(
await readFile(join(sourceRoot, "campaign.json"), "utf8"),
);
if (!/^gha-[1-9][0-9]*-[1-9][0-9]*$/.test(campaign.campaignId ?? ""))
throw new Error("Expected an original Actions campaign");
const program = join(
evalsRoot,
"evals/paperclip-runner/tools/eval_program.py",
);
const rendererDigest = createHash("sha256")
.update(await readFile(program))
.digest("hex");
await mkdir(outputRoot, { recursive: true });
const runsRoot = join(outputRoot, "public-runs");
const reportRoot = join(outputRoot, "report");
await sanitizeProtocolEvalRuns({
runsRoot: join(sourceRoot, "runs"),
publicRunsRoot: runsRoot,
});
execFileSync(
"python3",
[
program,View on GitHub (pinned to 01ad858492)
Solutions
- Point --source at the original Actions-produced campaign directory containing a gha-<run>-<n> campaignId
- If the source is a refresh output, re-run from the original sourceRoot instead
- Fix or regenerate campaign.json so campaignId follows the gha-<number>-<number> format
- Confirm campaignId is present and not undefined in campaign.json
Example fix
// before
// campaign.json: { "campaignId": "my-local-campaign" }
// after
// campaign.json: { "campaignId": "gha-10293847-1" } Defensive patterns
Strategy: validation
Validate before calling
const campaign = JSON.parse(await readFile(join(sourceRoot,'campaign.json'),'utf8')); if (!/^gha-[1-9][0-9]*-[1-9][0-9]*$/.test(campaign.campaignId ?? '')) throw new Error('Not an original Actions campaign: ' + campaign.campaignId); Type guard
const isActionsCampaign = (c) => typeof c?.campaignId === 'string' && /^gha-[1-9][0-9]*-[1-9][0-9]*$/.test(c.campaignId);
Try / catch
try { await refreshProtocolEvalReport(opts); } catch (e) { if (e.message === 'Expected an original Actions campaign') console.error('campaign.json in', opts.sourceRoot, 'has a non-Actions campaignId'); throw e; } Prevention
- Always refresh from the original Actions snapshot, not refresh outputs
- Never hand-edit campaignId
- Assert the campaignId shape in CI before invoking the refresher
When it happens
Trigger: Pointing --source at a local, synthetic, or refreshed campaign directory whose campaignId doesn't start with 'gha-' followed by two numeric segments; campaign.json missing campaignId (undefined fails the regex).
Common situations: Running the refresher against a previously refreshed report's output instead of the original campaign snapshot; editing campaignId by hand; a legacy campaign predating the gha- naming scheme.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid published campaign ID
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- "tool" is required and must be a string
- "runContext" is required and must be an object
- "runContext" must include agentId, runId, companyId, and pro
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/e8b6332e4e0d89ce.
Report an issue: GitHub.