paperclipai/paperclip · error · UnsafeChatPublicationError
External chat publications support at most
Error message
External chat publications support at most ${MAX_ATTACHMENTS} attachments What it means
projectAttachmentIds validates the attachmentIds array of a chat publication before it is sent to an external provider. It enforces MAX_ATTACHMENTS (20, chat-publication-projection.ts:13). If more than 20 attachment ids are supplied it throws UnsafeChatPublicationError, because external chat publications are capped at 20 attachments. The projection is the single boundary that accepts attachments, so this check guards the provider-bound payload.
Solutions
- Trim attachmentIds to the 20 most relevant before calling projectSafeChatPublication
- Batch the publication: send attachments in chunks of at most 20 across multiple publications
- Attach a single bundle (zip) instead of many individual files
- Fix upstream accumulation code that appends ids without a limit
Example fix
// before
await publish({ classification: "external", source: "explicit_board_send", text, attachmentIds: allIds });
// after
const MAX_ATTACHMENTS = 20;
await publish({
classification: "external",
source: "explicit_board_send",
text,
attachmentIds: allIds.slice(0, MAX_ATTACHMENTS),
}); Defensive patterns
Strategy: validation
Validate before calling
const MAX_ATTACHMENTS = 20;
if (attachmentIds && attachmentIds.length > MAX_ATTACHMENTS) {
attachmentIds = attachmentIds.slice(0, MAX_ATTACHMENTS);
} Type guard
function isWithinAttachmentLimit(ids: readonly string[] | null | undefined): ids is readonly string[] {
return !!ids && ids.length <= 20;
} Try / catch
try {
const payload = projectSafeChatPublication({ classification: "external", source, text, attachmentIds });
} catch (err) {
if (err instanceof UnsafeChatPublicationError && /at most 20 attachments/.test(err.message)) {
const payload = projectSafeChatPublication({ classification: "external", source, text, attachmentIds: attachmentIds!.slice(0, 20) });
} else throw err;
} Prevention
- Enforce the 20-attachment cap in UI bulk-select and upload flows
- Chunk publications when an operation legitimately produces many attachments
- Bundle many files into a single zip attachment
- Log when attachments are dropped so users know content was truncated
When it happens
Trigger: Calling projectSafeChatPublication with attachmentIds containing 21 or more string ids. Examples: an agent comment attaching every file touched in a large change set; a loop that accumulates attachment ids without a cap; an API client passing an unfiltered list of uploaded asset ids.
Common situations: An automation attaches all artifacts from a long-running task; a migration script forwards a legacy issue's full attachment history; a UI bulk-select that has no client-side limit sends 30 screenshots with one publication.
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
- External chat text exceeds its projected processing limit
- paperclip_runner_attachment_staging_count_denied
- Attachment exceeds the configured size limit
- CHAT_PROVIDER_PRETRANSPORT_REJECTED
- Completion cites no registered attachment on this task. Use…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/bb771698d2231c25.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/chat-publication-projection.ts:278
.replace(/[ \t]+\n/g, "\n")
.replace(/\n{3,}/g, "\n\n")
.trim();
if (!output) return "Update available in Paperclip.";
if (output.length > MAX_TEXT_OUTPUT_LENGTH) {
throw new UnsafeChatPublicationError(
"External chat text exceeds its projected processing limit",
);
}
return output;
}
function projectAttachmentIds(
input: readonly string[] | null | undefined,
): string[] | undefined {
if (!input?.length) return undefined;
if (input.length > MAX_ATTACHMENTS) {
throw new UnsafeChatPublicationError(
`External chat publications support at most ${MAX_ATTACHMENTS} attachments`,
);
}
const output: string[] = [];
const seen = new Set<string>();
for (const id of input) {
const normalized = id.trim().toLowerCase();
if (!UUID_RE.test(normalized)) {
throw new UnsafeChatPublicationError(
"External chat attachment ids must be UUIDs",
);
}
if (!seen.has(normalized)) {
seen.add(normalized);
output.push(normalized);
}
}View on GitHub (pinned to 3f1d897a7c)