paperclipai/paperclip · error · Error
Unsafe historical campaign ID
Error message
Unsafe historical campaign ID
What it means
While enriching history, the publisher downloads each historical campaign.json from S3; before doing so it re-validates every campaign ID against SAFE_CAMPAIGN so a tampered or malformed ID in history.json cannot be turned into an arbitrary S3 key or temp filename. An ID failing the gha-N-N(-report-slug) pattern triggers this error.
Source
Thrown at packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs:486
const temporary = await mkdtemp(
join(tmpdir(), "runner-protocol-eval-history-"),
);
const historyKey = `${validatedDestination.prefix}/history.json`;
const mergedHistory = mergeProtocolEvalHistory(
(await downloadJson(
validatedDestination.bucket,
historyKey,
join(temporary, "history.json"),
)) ?? emptyProtocolEvalHistory(),
protocolEvalHistoryRecord(
campaign,
`${validatedDestination.publicBaseUrl}/${validatedDestination.prefix}`,
),
);
const history = await enrichProtocolEvalHistory(mergedHistory, {
currentCampaign: campaign,
loadCampaign: async (id) => {
if (!SAFE_CAMPAIGN.test(id)) throw new Error("Unsafe historical campaign ID");
return downloadJson(validatedDestination.bucket,
`${validatedDestination.prefix}/campaigns/${id}/campaign.json`,
join(temporary, `${id}.json`));
},
});
const campaignPrefix = `${validatedDestination.prefix}/campaigns/${campaign.campaignId}`;
const manifestKey = `${campaignPrefix}/bundle-manifest.json`;
const existing = await downloadJson(
validatedDestination.bucket,
manifestKey,
join(temporary, "existing-manifest.json"),
);
if (existing && existing.bundleDigest !== manifest.bundleDigest) {
throw new Error(
`Immutable campaign ${campaign.campaignId} already exists with a different digest`,
);
}
if (!existing) {View on GitHub (pinned to 01ad858492)
Solutions
- Inspect s3://<bucket>/<prefix>/history.json and find the campaign entry with the non-conforming campaignId
- Remove or migrate that entry to a SAFE_CAMPAIGN-compliant ID, or reset history.json to a fresh v1 document
- Regenerate the offending campaign record through the current publisher so it gets a valid ID
- If it came from enrichment/analytics data, fix the upstream source of the ID
Example fix
// before (history.json)
{ "campaignId": "../../etc", ... }
// after (history.json)
{ "campaignId": "gha-12-1", ... } Defensive patterns
Strategy: validation
Validate before calling
const SAFE_CAMPAIGN = /^gha-[1-9][0-9]*-[1-9][0-9]*(-report-[a-z0-9][a-z0-9-]{0,39})?$/;
for (const c of history.campaigns)
if (!SAFE_CAMPAIGN.test(c.campaignId)) throw new Error(`remote history has unsafe id: ${c.campaignId}`); Type guard
const isSafeCampaignId = (v) => typeof v === "string" && /^gha-[1-9][0-9]*-[1-9][0-9]*(-report-[a-z0-9][a-z0-9-]{0,39})?$/.test(v); Try / catch
try {
await publishProtocolEvalHistory({ reportRoot, destination, viewerRoot });
} catch (e) {
if (String(e.message).includes("Unsafe historical campaign ID")) {
console.error("download history.json, fix/remove the malformed campaignId, and re-upload");
}
throw e;
} Prevention
- Never hand-edit history.json; only write it through this publisher
- Audit remote history.json entries after any schema/tooling migration
- Keep campaign IDs in the SAFE_CAMPAIGN format at creation time
When it happens
Trigger: history.json in the bucket containing a campaign entry with a malformed campaignId (hand-edited, produced by a bug, or from an older schema); injection attempts via IDs containing slashes, dot-dot segments, or unusual characters stored in the remote history.
Common situations: A legacy history.json with differently-formatted IDs from before the SAFE_CAMPAIGN convention; manual edits to history.json; corrupted analytics/enrichment data feeding IDs into loadCampaign.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- sandbox runtime asset key is not a simple path segment: ${ke
- Invalid GitHub launcher run ID
- Unexpected trusted viewer asset
- Public attempt must contain the read-only public chat projec
- Public replay contains private provider metadata
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/aa738a45dbc7c823.
Report an issue: GitHub.