paperclipai/paperclip · error · UnsafeChatPublicationError
External chat text exceeds its processing limit
Error message
External chat text exceeds its processing limit
What it means
projectSafeChatPublicationText is the only text path allowed from Paperclip into an external chat provider, and it bounds its sanitization work by rejecting inputs over MAX_TEXT_INPUT_LENGTH (1,000,000 UTF-16 units) with an UnsafeChatPublicationError. The limit protects against pathological redaction/regex workloads; it bounds processing, not delivery — shorter text may still expand during redaction and that is allowed.
Solutions
- Truncate or summarize the text below 1,000,000 characters before projecting (keep the most recent/relevant portion).
- Split the content into multiple smaller chat messages.
- Attach large content as files/attachments (MAX_ATTACHMENTS = 20) instead of inline text.
- If this happens systematically, fix the producer that concatenates unbounded content into one publication.
Example fix
// before await enqueueResponse(issueId, wholeThreadText); // throws if > 1M chars // after const MAX = 1_000_000; const text = wholeThreadText.length > MAX ? "…" + wholeThreadText.slice(-MAX + 1000) // keep the tail, note truncation : wholeThreadText; await enqueueResponse(issueId, text);
Defensive patterns
Strategy: validation
Validate before calling
const MAX = 1_000_000;
if (text.length > MAX) {
text = text.slice(0, MAX - 20) + "\n[truncated]"; // or split into parts
} Type guard
null
Try / catch
try { return projectSafeChatPublicationText(input); }
catch (e) {
if (e instanceof UnsafeChatPublicationError || e.message.includes("exceeds its processing limit")) {
return projectSafeChatPublicationText(truncate(input, 1_000_000));
}
throw e;
} Prevention
- Bound producer output before it reaches the projection layer
- Split very large content into multiple messages or attachments
- Summarize logs/threads instead of forwarding them verbatim
- Alert on message-size growth in agent response pipelines
When it happens
Trigger: Calling projectSafeChatPublicationText (directly or via receipt/enqueueResponse/projected/title/body helpers) with input text whose .length exceeds 1,000,000 — e.g. concatenating an entire issue thread or pasting a huge log dump as a chat reply.
Common situations: An automation forwarding giant build logs or file dumps to a chat provider; a runaway agent appending unbounded context into the response text; aggregating many comments into one message body.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- ACPX provider package name is invalid
- ACPX snapshot exceeds its byte bound
- Artifact exceeds size limit.
- Attachment exceeds the configured size limit
- Bridge response body exceeded the configured size limit.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/1486d8d81567d062.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/chat-publication-projection.ts:245
(_match, label: string, separator: string) =>
`${label}${separator}[REDACTED]`,
)
.replace(CONNECTION_STRING_RE, "[REDACTED]");
}
function truncateByCodePoint(input: string, limit: number): string {
if (input.length <= limit) return input;
return Array.from(input).slice(0, limit).join("");
}
/**
* The only text projection allowed to cross from Paperclip into a provider.
* It strips internal reasoning/tool/log content, redacts credentials, removes
* dangerous or token-bearing links, and neutralizes provider-wide mentions.
*/
export function projectSafeChatPublicationText(input: string): string {
if (input.length > MAX_TEXT_INPUT_LENGTH) {
throw new UnsafeChatPublicationError(
"External chat text exceeds its processing limit",
);
}
let output = input.replace(/<\|[^|\r\n]{1,80}\|>/g, "");
for (const pattern of HIDDEN_BLOCKS) output = output.replace(pattern, "");
output = stripHiddenSections(output);
// Strip token-bearing query strings before the general credential scanner.
// That scanner deliberately consumes uncertain unquoted values aggressively;
// running it first could eat the visible prose following a Markdown URL.
output = sanitizeUrls(output);
output = sanitizeCredentialText(output);
output = output
.replace(SLACK_BROADCAST_RE, (_match, name: string) => `@\u200b${name}`)
.replace(PROVIDER_BROADCAST_RE, (_match, name: string) => `@\u200b${name}`)
.replace(/[ \t]+\n/g, "\n")
.replace(/\n{3,}/g, "\n\n")
.trim();
View on GitHub (pinned to 3f1d897a7c)