{"record":{"id":"c74e583773b5a3e3","repo":"paperclipai/paperclip","slug":"external-chat-text-exceeds-its-projected-processing-limit","errorCode":null,"errorMessage":"External chat text exceeds its projected processing limit","messagePattern":"External chat text exceeds its projected processing limit","errorType":"validation","errorClass":"UnsafeChatPublicationError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-publication-projection.ts","lineNumber":266,"sourceCode":"  }\n  let output = input.replace(/<\\|[^|\\r\\n]{1,80}\\|>/g, \"\");\n  for (const pattern of HIDDEN_BLOCKS) output = output.replace(pattern, \"\");\n  output = stripHiddenSections(output);\n  // Strip token-bearing query strings before the general credential scanner.\n  // That scanner deliberately consumes uncertain unquoted values aggressively;\n  // running it first could eat the visible prose following a Markdown URL.\n  output = sanitizeUrls(output);\n  output = sanitizeCredentialText(output);\n  output = output\n    .replace(SLACK_BROADCAST_RE, (_match, name: string) => `@\\u200b${name}`)\n    .replace(PROVIDER_BROADCAST_RE, (_match, name: string) => `@\\u200b${name}`)\n    .replace(/[ \\t]+\\n/g, \"\\n\")\n    .replace(/\\n{3,}/g, \"\\n\\n\")\n    .trim();\n\n  if (!output) return \"Update available in Paperclip.\";\n  if (output.length > MAX_TEXT_OUTPUT_LENGTH) {\n    throw new UnsafeChatPublicationError(\n      \"External chat text exceeds its projected processing limit\",\n    );\n  }\n  return output;\n}\n\nfunction projectAttachmentIds(\n  input: readonly string[] | null | undefined,\n): string[] | undefined {\n  if (!input?.length) return undefined;\n  if (input.length > MAX_ATTACHMENTS) {\n    throw new UnsafeChatPublicationError(\n      `External chat publications support at most ${MAX_ATTACHMENTS} attachments`,\n    );\n  }\n\n  const output: string[] = [];\n  const seen = new Set<string>();","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/chat-publication-projection.ts#L248-L284","documentation":"projectSafeChatPublicationText sanitizes text before it leaves Paperclip for an external chat provider (Slack, Discord, etc.). After stripping hidden reasoning/tool sections, redacting credentials, and neutralizing unsafe links and broadcast mentions, it enforces a projected output cap of MAX_TEXT_OUTPUT_LENGTH (4,000,000 UTF-16 units, chat-publication-projection.ts:12). If the sanitized output still exceeds that cap, it throws UnsafeChatPublicationError so oversized text is never published. The input cap is 1,000,000 units; expansion comes from redaction markers and mention neutralization, so this error fires when the input is large or grows substantially during sanitization.","triggerScenarios":"Calling projectSafeChatPublicationText (directly or via projectSafeChatPublication) with a text string whose sanitized output exceeds 4,000,000 UTF-16 units. Realistic cases: input near the 1,000,000-unit input cap where credential redaction ('[REDACTED]' replacements), broadcast neutralization ('@' plus zero-width space), and hidden-section removal boundaries expand the text ~4x; or an input already above 1,000,000 units would hit the earlier input-limit error instead, so this specific error means the text passed the input check but grew past the output cap during projection.","commonSituations":"An agent comment dumps a huge log file or build output as its message; a milestone title/body concatenation balloons after redaction markers are inserted; a backfill script pushing large document text through explicit_board_send; a provider adapter passing an entire conversation transcript as publication text.","solutions":["Truncate the text before calling the projection function so post-sanitization output stays under 4,000,000 units","Move the bulk content (logs, transcripts) into attachments and publish a short summary text instead","Pre-strip the hidden/log sections yourself (the same patterns the projector strips) so redaction expansion does not push output over the cap","Check upstream callers for accidental concatenation of large payloads into a single publication text"],"exampleFix":"// before\nawait publish({ classification: \"external\", source: \"agent_comment\", text: hugeLog });\n// after\nconst MAX_SAFE_TEXT = 900_000;\nconst text = hugeLog.length > MAX_SAFE_TEXT\n  ? hugeLog.slice(0, MAX_SAFE_TEXT) + \"\\n... (truncated; see attachments)\"\n  : hugeLog;\nawait publish({ classification: \"external\", source: \"agent_comment\", text, attachmentIds: [logAttachmentId] });","handlingStrategy":"validation","validationCode":"const MAX_PROJECTED_OUTPUT = 4_000_000;\nconst MAX_SAFE_INPUT = 900_000; // headroom for redaction expansion\nif (typeof text !== \"string\") throw new TypeError(\"text must be a string\");\nif (text.length > MAX_SAFE_INPUT) {\n  text = text.slice(0, MAX_SAFE_INPUT) + \"\\n... (truncated)\";\n}","typeGuard":"function isWithinPublicationLimits(text: unknown): text is string {\n  return typeof text === \"string\" && text.length <= 900_000;\n}","tryCatchPattern":"try {\n  const payload = projectSafeChatPublication({ classification: \"external\", source, text });\n} catch (err) {\n  if (err instanceof UnsafeChatPublicationError && /projected processing limit/.test(err.message)) {\n    payload = projectSafeChatPublication({ classification: \"external\", source, text: truncateForPublication(text) });\n  } else throw err;\n}","preventionTips":["Cap agent-generated text well below 1,000,000 units before publication","Send large logs/files as attachments instead of inline text","Remember redaction markers expand text; leave at least 4x headroom against the 4,000,000 output cap","Add a test with maximum-size input that includes credentials and mentions (worst-case expansion)"],"tags":["validation","limit-exceeded","chat","sanitization"],"backgroundTag":"payload-too-large","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}