{"record":{"id":"63748cc190206ef9","repo":"paperclipai/paperclip","slug":"report-campaign-id-does-not-match-publication-targ","errorCode":null,"errorMessage":"Report campaign ID does not match publication target","messagePattern":"Report campaign ID does not match publication target","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs","lineNumber":254,"sourceCode":"  ) {\n    throw new Error(\"Public report campaign metadata is invalid\");\n  }\n  return { files, campaign };\n}\n\nexport async function createProtocolEvalBundleManifest(\n  reportRoot,\n  campaignId,\n  { viewerRoot } = {},\n) {\n  if (!SAFE_CAMPAIGN.test(campaignId))\n    throw new Error(\"Unsafe protocol eval campaign ID\");\n  const { files, campaign } = await validatePublicProtocolEvalReport(\n    reportRoot,\n    { viewerRoot },\n  );\n  if (campaign.campaignId !== campaignId)\n    throw new Error(\"Report campaign ID does not match publication target\");\n  const entries = await Promise.all(\n    files.map(async (file) => {\n      const absolute = resolve(reportRoot, ...file.split(\"/\"));\n      const [content, metadata] = await Promise.all([\n        readFile(absolute),\n        stat(absolute),\n      ]);\n      return {\n        path: file,\n        sha256: createHash(\"sha256\").update(content).digest(\"hex\"),\n        bytes: metadata.size,\n      };\n    }),\n  );\n  return {\n    schema: \"paperclip.runner-protocol-eval.bundle/v1\",\n    campaignId,\n    bundleDigest: createHash(\"sha256\")","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs#L236-L272","documentation":"After validating the report, createProtocolEvalBundleManifest cross-checks that the campaignId embedded in the report's campaign.json equals the campaignId argument the caller asked to publish for. A mismatch means the manifest would be filed under a different campaign than the report declares, breaking the immutable bundle addressing, so it throws.","triggerScenarios":"Calling createProtocolEvalBundleManifest(reportRoot, 'gha-9-1') against a reportRoot whose campaign.json says campaignId 'gha-8-2'; reusing a stale report directory while passing the current run's ID; publishing two different generated reports with a hard-coded campaign ID.","commonSituations":"CI retries that reuse a cached report from another attempt; locally testing the publish script with the wrong report directory; a copied/moved report directory containing another run's metadata.","solutions":["Pass the campaign ID exactly as written in <reportRoot>/campaign.json","Regenerate the report for the current run so its campaign.json matches the publication target","Point reportRoot at the correct report directory for the given campaignId","Read the report's campaign.json first and use its campaignId as the manifest argument"],"exampleFix":"// before\nawait createProtocolEvalBundleManifest(\"reports/old-run\", \"gha-99-1\");\n// after\nconst campaign = JSON.parse(await readFile(\"reports/old-run/campaign.json\", \"utf8\"));\nawait createProtocolEvalBundleManifest(\"reports/old-run\", campaign.campaignId);","handlingStrategy":"validation","validationCode":"const campaign = JSON.parse(fs.readFileSync(path.join(reportRoot, \"campaign.json\"), \"utf8\"));\nif (campaign.campaignId !== targetCampaignId) throw new Error(`report is for ${campaign.campaignId}, not ${targetCampaignId}`);","typeGuard":null,"tryCatchPattern":"try {\n  await createProtocolEvalBundleManifest(reportRoot, campaignId);\n} catch (e) {\n  if (String(e.message).includes(\"does not match publication target\")) {\n    const declared = JSON.parse(fs.readFileSync(path.join(reportRoot, \"campaign.json\"), \"utf8\")).campaignId;\n    console.error(`report declares ${declared}; pass that id or regenerate the report`);\n  }\n  throw e;\n}","preventionTips":["Always read campaign.json and use its campaignId as the publication target","Do not reuse cached report directories across CI attempts","Regenerate the report whenever the intended campaign ID changes"],"tags":["validation","consistency","publishing"],"backgroundTag":"invalid-argument-value","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"}