{"record":{"id":"0243c6b8af7c951e","repo":"paperclipai/paperclip","slug":"public-replay-contains-a-raw-tool-payload","errorCode":null,"errorMessage":"Public replay contains a raw tool payload","messagePattern":"Public replay contains a raw tool payload","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/public-eval-viewer.mjs","lineNumber":222,"sourceCode":"      const shapes = {\n        user_message: \"kind id at author body streaming\",\n        agent_message: \"kind id at author body streaming\",\n        tool_activity:\n          \"kind id at operationId status summary input result evidenceRef\",\n        system_notice: \"kind id at glyph text evidenceRef\",\n      };\n      fields(item, shapes[item.kind]);\n      if (item.evidenceRef) fields(item.evidenceRef, \"section recordId\");\n      if (\n        item.kind === \"tool_activity\" &&\n        (JSON.stringify(item.input) !==\n          JSON.stringify({\n            detail: \"Arguments withheld from public replay.\",\n          }) ||\n          Object.keys(item.result).sort().join(\",\") !== \"detail,outcome\" ||\n          item.result.detail !== \"Tool payload withheld from public replay.\")\n      )\n        throw new Error(\"Public replay contains a raw tool payload\");\n    }\n  }\n}\n\nexport function validatePublicViewerPage(content, trustedIndex) {\n  const match = content.match(PUBLIC_VIEWER_DATA);\n  if (!match || publicViewerShell(trustedIndex, match[1]) !== content)\n    throw new Error(\"Public viewer page differs from the trusted shell\");\n  const payload = JSON.parse(match[1]);\n  validatePublicChatPayload(payload);\n  return payload;\n}\n","sourceCodeStart":204,"sourceCodeEnd":235,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/public-eval-viewer.mjs#L204-L235","documentation":"Thrown by validatePublicChatPayload when a tool_activity item's projected result is not the redacted stub: it must serialize to {\"detail\":\"Arguments withheld from public replay.\"} style markers with keys exactly outcome,detail and detail equal to 'Tool payload withheld from public replay.'. Anything else means raw tool arguments or output ended up in the public replay.","triggerScenarios":"Validating a payload where a tool_activity item's result is JSON.stringify'd differently, has keys other than {detail,outcome}, or carries a non-sentinel detail string.","commonSituations":"Skipping the input/result stubbing step for tool items; a projector bug passing through item.result verbatim; manual JSON edits reintroducing raw payloads.","solutions":["Apply the tool-item stubbing so input is the withheld-arguments marker and result is { outcome, detail: 'Tool payload withheld from public replay.' }.","Inspect the projector's tool_activity branch for fields passed through unredacted.","Regenerate the public replay rather than patching the JSON by hand."],"exampleFix":"// before\nitem.result = toolResult; item.input = toolArgs;\n// after\nitem.input = { detail: 'Arguments withheld from public replay.' };\nitem.result = { outcome: toolResult.outcome, detail: 'Tool payload withheld from public replay.' };","handlingStrategy":"type-guard","validationCode":"const ok = item.input && JSON.stringify(item.input) === JSON.stringify({detail:'Arguments withheld from public replay.'}) && item.result?.detail === 'Tool payload withheld from public replay.';","typeGuard":"function isStubbedToolItem(i) { return i?.kind === 'tool_activity' && i.result?.detail === 'Tool payload withheld from public replay.' && Object.keys(i.result ?? {}).sort().join(',') === 'detail,outcome'; }","tryCatchPattern":null,"preventionTips":["Construct tool stubs from a shared helper rather than inline object literals.","Snapshot-test serialized tool_activity items to catch raw payload leaks.","Never assign raw tool args/results to public view items."],"tags":["validation","security","redaction"],"backgroundTag":"unexpected-response-shape","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}