paperclipai/paperclip · error · Error

Public replay contains a raw tool payload

Error message

Public replay contains a raw tool payload

What it means

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.

Source

Thrown at packages/paperclip-runner/scripts/public-eval-viewer.mjs:222

      const shapes = {
        user_message: "kind id at author body streaming",
        agent_message: "kind id at author body streaming",
        tool_activity:
          "kind id at operationId status summary input result evidenceRef",
        system_notice: "kind id at glyph text evidenceRef",
      };
      fields(item, shapes[item.kind]);
      if (item.evidenceRef) fields(item.evidenceRef, "section recordId");
      if (
        item.kind === "tool_activity" &&
        (JSON.stringify(item.input) !==
          JSON.stringify({
            detail: "Arguments withheld from public replay.",
          }) ||
          Object.keys(item.result).sort().join(",") !== "detail,outcome" ||
          item.result.detail !== "Tool payload withheld from public replay.")
      )
        throw new Error("Public replay contains a raw tool payload");
    }
  }
}

export function validatePublicViewerPage(content, trustedIndex) {
  const match = content.match(PUBLIC_VIEWER_DATA);
  if (!match || publicViewerShell(trustedIndex, match[1]) !== content)
    throw new Error("Public viewer page differs from the trusted shell");
  const payload = JSON.parse(match[1]);
  validatePublicChatPayload(payload);
  return payload;
}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Apply the tool-item stubbing so input is the withheld-arguments marker and result is { outcome, detail: 'Tool payload withheld from public replay.' }.
  2. Inspect the projector's tool_activity branch for fields passed through unredacted.
  3. Regenerate the public replay rather than patching the JSON by hand.

Example fix

// before
item.result = toolResult; item.input = toolArgs;
// after
item.input = { detail: 'Arguments withheld from public replay.' };
item.result = { outcome: toolResult.outcome, detail: 'Tool payload withheld from public replay.' };
Defensive patterns

Strategy: type-guard

Validate before calling

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.';

Type guard

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'; }

Prevention

When it happens

Trigger: 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.

Common situations: Skipping the input/result stubbing step for tool items; a projector bug passing through item.result verbatim; manual JSON edits reintroducing raw payloads.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/0243c6b8af7c951e. Report an issue: GitHub.