paperclipai/paperclip · error · UnsafeChatPublicationError

External chat action style is invalid

Error message

External chat action style is invalid

What it means

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.

Solutions

  1. Use only "default", "primary", or "danger" for action.style (matching is exact and case-sensitive)
  2. Map your app's severity/style vocabulary to the allowed set before constructing the card
  3. Omit the style field entirely when no explicit emphasis is needed (it is optional)
  4. Add a whitelist check on style where the card is built

Example fix

// before
style: severity === "critical" ? "alert" : "normal"
// after
style: severity === "critical" ? "danger" : "default" // one of "default"|"primary"|"danger"
Defensive patterns

Strategy: type-guard

Validate before calling

const CARD_ACTION_STYLES = new Set(["default", "primary", "danger"]);
if (action.style !== undefined && !CARD_ACTION_STYLES.has(action.style)) {
  action.style = undefined; // or normalize to "default"
}

Type guard

type CardActionStyle = "default" | "primary" | "danger";
function isCardActionStyle(v: unknown): v is CardActionStyle {
  return v === "default" || v === "primary" || v === "danger";
}

Try / catch

try {
  const payload = projectSafeChatPublication(input);
} catch (err) {
  if (err instanceof UnsafeChatPublicationError && err.message === "External chat action style is invalid") {
    for (const a of input.interaction.card.actions) {
      if (a.type === "callback" && !isCardActionStyle(a.style)) delete a.style;
    }
    return projectSafeChatPublication(input);
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/8692f3d36ab0fe7c. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/chat-publication-projection.ts:339

    throw new UnsafeChatPublicationError(
      `External chat cards support at most ${MAX_CARD_ACTIONS} actions`,
    );
  }

  const actions: SafeExternalChatCardAction[] = [];
  for (const action of rawActions) {
    const label = truncateByCodePoint(
      projectSafeChatPublicationText(action.label),
      MAX_ACTION_LABEL_LENGTH,
    );
    if (action.type === "callback") {
      if (!SAFE_IDENTIFIER_RE.test(action.actionId)) {
        throw new UnsafeChatPublicationError(
          "External chat action id is invalid",
        );
      }
      if (action.style && !CARD_ACTION_STYLES.has(action.style)) {
        throw new UnsafeChatPublicationError(
          "External chat action style is invalid",
        );
      }
      actions.push({
        type: "callback",
        actionId: action.actionId,
        label,
        ...(action.style ? { style: action.style } : {}),
      });
      continue;
    }

    if (action.type !== "link") {
      throw new UnsafeChatPublicationError(
        "External chat card action type is invalid",
      );
    }

View on GitHub (pinned to 3f1d897a7c)