paperclipai/paperclip · error · ValidationError
VALIDATION_ERROR
VALIDATION_ERROR
Error message
Invalid durable publication text transport
What it means
renderPublicationTransportText/rendered/splitNativePublicationText only accept persisted publication text that matches a strict fenced-wrapper grammar (PREFIX/SUFFIX regexes on a ``` or ~~~ fence with an optional short extension tag). invalidTransport throws a ValidationError with code VALIDATION_ERROR when the stored text cannot be parsed as such a wrapper, so arbitrary persisted prose, links, or credentials are never treated as a transport envelope.
Source
Thrown at server/src/services/chat-publication-text-parts.ts:25
} from "chat";
import type { SafeChatPublicationPayload } from "@paperclipai/shared";
type NativeTextProvider = "slack" | "github" | "microsoft-teams";
export interface NativePublicationTextPart {
text: string;
prefix?: string;
suffix?: string;
}
const slack = new SlackFormatConverter();
const teams = new TeamsFormatConverter();
const MAX_WRAPPER_LENGTH = 292;
const PREFIX = /^(`{3,256}|~{3,256})([A-Za-z0-9_+.-]{0,32})\n$/;
const SUFFIX = /^\n(`{3,256}|~{3,256})\n?$/;
function invalidTransport(): never {
throw Object.assign(new Error("Invalid durable publication text transport"), {
name: "ValidationError",
code: "VALIDATION_ERROR",
});
}
/** Never accept arbitrary persisted prose, links, or credentials as a wrapper. */
export function renderPublicationTransportText(
payload: SafeChatPublicationPayload,
): string {
const part = payload.transportPart;
const prefix: unknown = part?.prefix;
const suffix: unknown = part?.suffix;
if (prefix === undefined && suffix === undefined) return payload.text;
if (part?.mode && part.mode !== "inline") invalidTransport();
if (
prefix !== undefined &&
(typeof prefix !== "string" ||
prefix.length > MAX_WRAPPER_LENGTH ||
!PREFIX.test(prefix))
)View on GitHub (pinned to 01ad858492)
Solutions
- Rewrite the persisted publication text so it is a complete fenced wrapper: open fence line (3-256 backticks/tildes plus optional tag + newline), payload, then closing fence line.
- Use splitNativePublicationText/renderPublicationTransportText to regenerate the envelope from the payload instead of hand-authoring the stored text.
- If the row is legacy/unfenced, re-publish the payload through the normal publication path so a valid transport text is written.
- Verify the fence tag is only [A-Za-z0-9_+.-] and <=32 chars and the fence is <=256 marks; anything else is rejected.
Example fix
// before (stored text)
"some plain publication prose\n"
// after
"```json\n{\"title\":\"...\"}\n```\n" Defensive patterns
Strategy: try-catch
Validate before calling
const PREFIX = /^(`{3,256}|~{3,256})([A-Za-z0-9_+.-]{0,32})\n$/;
if (!PREFIX.test(firstLine) || !/^\n(`{3,256}|~{3,256})\n?$/.test(tail)) throw new Error("not a valid publication transport"); Type guard
function isPublicationTransport(text: string): boolean {
return text.startsWith("```") || text.startsWith("~~~");
} Try / catch
try {
const parts = splitNativePublicationText(stored);
} catch (e) {
if ((e as { code?: string }).code === "VALIDATION_ERROR") {
// re-publish payload through renderPublicationTransportText to rebuild a valid envelope
}
} Prevention
- Never hand-edit persisted publication text; always write it via renderPublicationTransportText.
- Keep fence lines exactly matching the PREFIX/SUFFIX grammar (fence length <=256, tag chars limited to [A-Za-z0-9_+.-], newline after the fence).
- Wrap reads of stored publication rows in try/catch and treat VALIDATION_ERROR as corrupt-legacy data needing re-publication.
- Add a migration/validation step that re-wraps or flags unfenced legacy rows.
When it happens
Trigger: Calling renderPublicationTransportText(payload), rendered(), or splitNativePublicationText() with a SafeChatPublicationPayload whose text does not end with a fence line matching /^(`{3,256}|~{3,256})([A-Za-z0-9_+.-]{0,32})\n$/ and close with the matching SUFFIX fence.
Common situations: A row in the DB was written by hand or by an older code path without the fenced transport wrapper; a migration or manual edit mangled the fence line (wrong backtick count, trailing spaces, missing newline after the fence); someone pasted plain markdown into the publication field.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- "tool" is required and must be a string
- "runContext" is required and must be an object
- "runContext" must include agentId, runId, companyId, and pro
- ${prefix}: the capability must be an object.
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/0b7de0ec8ad204e0.
Report an issue: GitHub.