{"record":{"id":"0b7de0ec8ad204e0","repo":"paperclipai/paperclip","slug":"validation-error-0b7de0","errorCode":"VALIDATION_ERROR","errorMessage":"Invalid durable publication text transport","messagePattern":"Invalid durable publication text transport","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-publication-text-parts.ts","lineNumber":25,"sourceCode":"} from \"chat\";\nimport type { SafeChatPublicationPayload } from \"@paperclipai/shared\";\n\ntype NativeTextProvider = \"slack\" | \"github\" | \"microsoft-teams\";\nexport interface NativePublicationTextPart {\n  text: string;\n  prefix?: string;\n  suffix?: string;\n}\nconst slack = new SlackFormatConverter();\nconst teams = new TeamsFormatConverter();\nconst MAX_WRAPPER_LENGTH = 292;\nconst PREFIX = /^(`{3,256}|~{3,256})([A-Za-z0-9_+.-]{0,32})\\n$/;\nconst SUFFIX = /^\\n(`{3,256}|~{3,256})\\n?$/;\n\nfunction invalidTransport(): never {\n  throw Object.assign(new Error(\"Invalid durable publication text transport\"), {\n    name: \"ValidationError\",\n    code: \"VALIDATION_ERROR\",\n  });\n}\n\n/** Never accept arbitrary persisted prose, links, or credentials as a wrapper. */\nexport function renderPublicationTransportText(\n  payload: SafeChatPublicationPayload,\n): string {\n  const part = payload.transportPart;\n  const prefix: unknown = part?.prefix;\n  const suffix: unknown = part?.suffix;\n  if (prefix === undefined && suffix === undefined) return payload.text;\n  if (part?.mode && part.mode !== \"inline\") invalidTransport();\n  if (\n    prefix !== undefined &&\n    (typeof prefix !== \"string\" ||\n      prefix.length > MAX_WRAPPER_LENGTH ||\n      !PREFIX.test(prefix))\n  )","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-publication-text-parts.ts#L7-L43","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (stored text)\n\"some plain publication prose\\n\"\n// after\n\"```json\\n{\\\"title\\\":\\\"...\\\"}\\n```\\n\"","handlingStrategy":"try-catch","validationCode":"const PREFIX = /^(`{3,256}|~{3,256})([A-Za-z0-9_+.-]{0,32})\\n$/;\nif (!PREFIX.test(firstLine) || !/^\\n(`{3,256}|~{3,256})\\n?$/.test(tail)) throw new Error(\"not a valid publication transport\");","typeGuard":"function isPublicationTransport(text: string): boolean {\n  return text.startsWith(\"```\") || text.startsWith(\"~~~\");\n}","tryCatchPattern":"try {\n  const parts = splitNativePublicationText(stored);\n} catch (e) {\n  if ((e as { code?: string }).code === \"VALIDATION_ERROR\") {\n    // re-publish payload through renderPublicationTransportText to rebuild a valid envelope\n  }\n}","preventionTips":["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."],"tags":["validation","parsing","chat-publication"],"backgroundTag":"invalid-argument-format","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}