paperclipai/paperclip · critical · Error

Public replay contains a private session identity

Error message

Public replay contains a private session identity

What it means

The visit() walker in validatePublicChatPayload() recursively scans every string value in the payload. Any string stored under a key ending in sessionId or providerSessionId (case-insensitive) must be exactly "public-report", "unknown", or "redacted"; anything else means an internal session identifier survived projection, and "Public replay contains a private session identity" is thrown.

Source

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

    "tools calls authorization control_plane runner state traceability parity",
  );
  for (const check of payload.checks) {
    fields(
      check,
      "id kind passed detail evidenceRefs title description definition anchor",
    );
    fields(check.definition, "id kind");
    fields(check.anchor, "kind id");
    if (check.evidenceRefs.length)
      throw new Error("Public replay contains raw evidence references");
  }
  const visit = (value, key = "") => {
    if (typeof value === "string") {
      if (
        /(?:sessionId|providerSessionId)$/i.test(key) &&
        !["public-report", "unknown", "redacted"].includes(value)
      )
        throw new Error("Public replay contains a private session identity");
      for (const pattern of SECRET_TEXT) {
        pattern.lastIndex = 0;
        if (pattern.test(value))
          throw new Error(
            "Public replay contains credential or private reference material",
          );
      }
    } else if (value && typeof value === "object") {
      for (const [name, child] of Object.entries(value)) {
        if (
          /^(?:managedProfile|acpxProfile|providerTrace|mockState|stateHistory|trace|environment|env|apiKey|accessToken|password|secret)$/i.test(
            name,
          ) &&
          child != null
        )
          throw new Error("Public replay contains a private field");
        visit(child, name);
      }

View on GitHub (pinned to 01ad858492)

Solutions

  1. Redact every sessionId/providerSessionId value in the projection to "public-report" (or "unknown"/"redacted") before validation.
  2. Locate the offending key path and extend the projector to redact that nested section, not just the top-level view.
  3. Confirm the projector's redaction matches the allowed values exactly (case-sensitive: "public-report", "unknown", "redacted").
  4. Regenerate the payload with the fixed projector and revalidate.

Example fix

// before
item: { id, sessionId: run.providerSessionId },
// after
item: { id, sessionId: "redacted" },
Defensive patterns

Strategy: validation

Validate before calling

const SESSION_OK = new Set(["public-report", "unknown", "redacted"]);
const walk = (v, key = "") => typeof v === "string"
  ? (/(?:sessionId|providerSessionId)$/i.test(key) && !SESSION_OK.has(v) && console.warn("unredacted session id at", key))
  : v && typeof v === "object" && Object.entries(v).forEach(([k, c]) => walk(c, k));
walk(payload);

Type guard

const sessionIdsRedacted = (payload) => { let ok = true; const walk = (v, k = "") => { if (typeof v === "string") { if (/(?:sessionId|providerSessionId)$/i.test(k) && !["public-report","unknown","redacted"].includes(v)) ok = false; } else if (v && typeof v === "object") Object.entries(v).forEach(([n, c]) => walk(c, n)); }; walk(payload); return ok; };

Try / catch

try {
  validatePublicChatPayload(payload);
} catch (err) {
  if (err.message === "Public replay contains a private session identity") {
    throw new Error("A nested sessionId survived projection; redact all sessionId/providerSessionId values before publishing");
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling validatePublicChatPayload with a payload containing any nested {sessionId: "<real-id>"} or {providerSessionId: "..."} — e.g. turns, evidence calls, or run blocks that retained their original session IDs instead of redacting to "public-report"/"unknown"/"redacted".

Common situations: A newly added payload section (e.g. a new turn item or call field) includes raw sessionId and the projector doesn't touch it; the redaction allowlist words were changed; nested objects deep in the tree bypassed the projector's top-level redaction.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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