{"record":{"id":"8692f3d36ab0fe7c","repo":"paperclipai/paperclip","slug":"external-chat-action-style-is-invalid","errorCode":null,"errorMessage":"External chat action style is invalid","messagePattern":"External chat action style is invalid","errorType":"validation","errorClass":"UnsafeChatPublicationError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-publication-projection.ts","lineNumber":339,"sourceCode":"    throw new UnsafeChatPublicationError(\n      `External chat cards support at most ${MAX_CARD_ACTIONS} actions`,\n    );\n  }\n\n  const actions: SafeExternalChatCardAction[] = [];\n  for (const action of rawActions) {\n    const label = truncateByCodePoint(\n      projectSafeChatPublicationText(action.label),\n      MAX_ACTION_LABEL_LENGTH,\n    );\n    if (action.type === \"callback\") {\n      if (!SAFE_IDENTIFIER_RE.test(action.actionId)) {\n        throw new UnsafeChatPublicationError(\n          \"External chat action id is invalid\",\n        );\n      }\n      if (action.style && !CARD_ACTION_STYLES.has(action.style)) {\n        throw new UnsafeChatPublicationError(\n          \"External chat action style is invalid\",\n        );\n      }\n      actions.push({\n        type: \"callback\",\n        actionId: action.actionId,\n        label,\n        ...(action.style ? { style: action.style } : {}),\n      });\n      continue;\n    }\n\n    if (action.type !== \"link\") {\n      throw new UnsafeChatPublicationError(\n        \"External chat card action type is invalid\",\n      );\n    }\n","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/chat-publication-projection.ts#L321-L357","documentation":"For callback-type card actions, an optional action.style must be one of the allowed CARD_ACTION_STYLES values: \"default\", \"primary\", or \"danger\". Any other string (or an invalid runtime value) throws UnsafeChatPublicationError, because the style is forwarded verbatim to the external chat provider payload.","triggerScenarios":"Setting a callback action's style to a value outside {\"default\",\"primary\",\"danger\"} — e.g. \"secondary\", \"destructive\", \"warning\", \"DANGER\" (case-sensitive), or a value typed from another design system's button-style enum.","commonSituations":"Reusing Slack/Teams button style enums (e.g. \"primary\" vs \"danger\" naming differences) directly in the card; mapping a UI severity like \"critical\" to a card style; a typo or casing mismatch after a rename.","solutions":["Use only \"default\", \"primary\", or \"danger\" for action.style (matching is exact and case-sensitive)","Map your app's severity/style vocabulary to the allowed set before constructing the card","Omit the style field entirely when no explicit emphasis is needed (it is optional)","Add a whitelist check on style where the card is built"],"exampleFix":"// before\nstyle: severity === \"critical\" ? \"alert\" : \"normal\"\n// after\nstyle: severity === \"critical\" ? \"danger\" : \"default\" // one of \"default\"|\"primary\"|\"danger\"","handlingStrategy":"type-guard","validationCode":"const CARD_ACTION_STYLES = new Set([\"default\", \"primary\", \"danger\"]);\nif (action.style !== undefined && !CARD_ACTION_STYLES.has(action.style)) {\n  action.style = undefined; // or normalize to \"default\"\n}","typeGuard":"type CardActionStyle = \"default\" | \"primary\" | \"danger\";\nfunction isCardActionStyle(v: unknown): v is CardActionStyle {\n  return v === \"default\" || v === \"primary\" || v === \"danger\";\n}","tryCatchPattern":"try {\n  const payload = projectSafeChatPublication(input);\n} catch (err) {\n  if (err instanceof UnsafeChatPublicationError && err.message === \"External chat action style is invalid\") {\n    for (const a of input.interaction.card.actions) {\n      if (a.type === \"callback\" && !isCardActionStyle(a.style)) delete a.style;\n    }\n    return projectSafeChatPublication(input);\n  }\n  throw err;\n}","preventionTips":["Define the style union as a shared TypeScript type so invalid values fail at compile time","Map external design-system severities to {default, primary, danger} at the boundary","Omit style rather than guessing a value when no emphasis is intended"],"tags":["validation","chat-publication","enum"],"backgroundTag":"invalid-enum-value","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"}