{"record":{"id":"ed3a432545e6fe02","repo":"paperclipai/paperclip","slug":"invalid-published-campaign-id","errorCode":null,"errorMessage":"Invalid published campaign ID","messagePattern":"Invalid published campaign ID","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs","lineNumber":554,"sourceCode":"  await writeFile(index, renderProtocolEvalHistoryIndex(history, stylesheetHref));\n  await uploadFile(\n    validatedDestination.bucket,\n    `${validatedDestination.prefix}/index.html`,\n    index,\n    \"no-cache\",\n  );\n  return {\n    campaignId: campaign.campaignId,\n    bundleDigest: manifest.bundleDigest,\n    historySize: history.campaigns.length,\n    reportUrl: `${validatedDestination.publicBaseUrl}/${campaignPrefix}/index.html`,\n    historyUrl: `${validatedDestination.publicBaseUrl}/${validatedDestination.prefix}/index.html`,\n  };\n}\n\nexport async function writeProtocolEvalPublicationLinks(result, environment = process.env) {\n  const { campaignId, reportUrl, historyUrl } = result;\n  if (!SAFE_CAMPAIGN.test(campaignId)) throw new Error(\"Invalid published campaign ID\");\n  const safeUrl = (value) => {\n    const url = new URL(value);\n    if (url.protocol !== \"https:\" || url.username || url.password || /[\\r\\n<>]/.test(value))\n      throw new Error(\"Invalid published report URL\");\n    return url.href;\n  };\n  const report = safeUrl(reportUrl);\n  const history = safeUrl(historyUrl);\n  if (environment.GITHUB_OUTPUT)\n    await appendFile(environment.GITHUB_OUTPUT, `report_url=${report}\\nhistory_url=${history}\\n`);\n  if (environment.GITHUB_STEP_SUMMARY)\n    await appendFile(environment.GITHUB_STEP_SUMMARY, `## Published Runner Evalbook\\n\\n[Open this run's Evalbook](<${report}>) · [All eval runs](<${history}>)\\n\\nCampaign: \\`${campaignId}\\`\\n\\nPublic replay uses the Runner Lab theme; full evidence is in the workflow artifact.\\n`);\n}\n\nasync function main() {\n  const result = await publishProtocolEvalHistory({\n    viewerRoot: process.env.PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR,\n    reportRoot: resolve(","sourceCodeStart":536,"sourceCodeEnd":572,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs#L536-L572","documentation":"writeProtocolEvalPublicationLinks publishes protocol eval history links and refuses to process campaign IDs that don't match SAFE_CAMPAIGN. The error means the campaignId extracted from the publish result was missing or contained characters outside the allowlisted format, so the script aborts before writing GitHub Actions outputs to avoid injecting unsafe values into the public site URL paths.","triggerScenarios":"Calling writeProtocolEvalPublicationLinks with a result whose campaignId is undefined, empty, or fails the SAFE_CAMPAIGN regex (e.g. contains slashes, uppercase, spaces, or is a full URL instead of a bare ID).","commonSituations":"A CI workflow passes a run identifier or branch name instead of the campaign ID; the upstream campaign.json has a malformed or legacy campaignId; the result object was built incorrectly and the field is undefined.","solutions":["Inspect the campaignId value passed in result and correct it to match SAFE_CAMPAIGN (e.g. lowercase alphanumerics/hyphens only)","Regenerate the campaign so campaign.json gets a properly formatted campaignId","Add a console log of result.campaignId before the throw to diagnose the actual value","Pass the campaign ID explicitly from the workflow step rather than deriving it from an untrusted source"],"exampleFix":"// before\nawait writeProtocolEvalPublicationLinks({ campaignId: process.env.GITHUB_RUN_ID, ... });\n// after\nawait writeProtocolEvalPublicationLinks({ campaignId: 'gha-12345-1', ... });","handlingStrategy":"validation","validationCode":"if (!/^[A-Za-z0-9_-]+$/.test(campaignId)) throw new Error('campaignId fails SAFE_CAMPAIGN: ' + campaignId);","typeGuard":"const hasSafeCampaignId = (r) => typeof r?.campaignId === 'string' && r.campaignId.length > 0;","tryCatchPattern":"try { await writeProtocolEvalPublicationLinks(result); } catch (e) { if (e.message === 'Invalid published campaign ID') { console.error('Bad campaignId:', result.campaignId); } throw e; }","preventionTips":["Log result.campaignId before publishing","Validate campaign IDs at generation time, not publish time","Never derive campaign IDs from branch names or free-form env vars"],"tags":["validation","ci","campaign-id"],"backgroundTag":"invalid-argument-format","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}