Skip to content

WhatsApp adapter adds visible backslashes before literal tildes #908

Description

@luksch42

Bug Description

In @chat-adapter/whatsapp@4.39.0, outgoing Markdown containing a literal tilde gets an extra backslash. For example, (~80 % easy) is delivered as (\~80 % easy), with the backslash visible in WhatsApp.

The original model output contains no backslash. This reproduces directly in the adapter’s formatter without an LLM or network request.

Steps to Reproduce

  1. Use @chat-adapter/whatsapp@4.39.0 with chat@4.37.0 (our project pins chat via an override).
  2. Run the minimal code sample below with Bun.
  3. Observe that the formatter returns (\~80 % easy) instead of (~80 % easy).
  4. Sending { markdown: "(~80 % easy)" } uses the same converter and includes the unwanted backslash in the outgoing message.

Expected Behavior

The formatter should return (~80 % easy) unchanged. Literal text should arrive without serializer-added Markdown escapes, while supported formatting still converts to WhatsApp syntax. Intentional backslashes and code content should remain intact.

Actual Behavior

The formatter returns (\~80 % easy). The added backslash remains visible in the delivered WhatsApp message.

This also affects multiple tildes: ~45 min, Pace ~6:29/km becomes \~45 min, Pace \~6:29/km.

Code Sample

import { WhatsAppAdapter } from "@chat-adapter/whatsapp";
import { ConsoleLogger, parseMarkdown } from "chat";

const adapter = new WhatsAppAdapter({
  accessToken: "test",
  appSecret: "test",
  phoneNumberId: "test",
  verifyToken: "test",
  userName: "test",
  logger: new ConsoleLogger("silent"),
});

console.log(adapter.renderFormatted(parseMarkdown("(~80 % easy)")));
// Expected: (~80 % easy)
// Actual:   (\~80 % easy)

Chat SDK Version

chat@4.37.0; @chat-adapter/whatsapp@4.39.0

Node.js Version

Not used for reproduction — Bun 1.4.0

Platform Adapter

WhatsApp

Operating System

macOS

Additional Context

The issue appears to originate in packages/adapter-whatsapp/src/markdown.ts:

  1. WhatsAppFormatConverter.fromAst() calls the shared stringifyMarkdown() helper.
  2. That helper uses remark-stringify with remark-gfm. Its strikethrough serializer escapes literal ~ characters.
  3. toWhatsAppFormat() converts bold and strikethrough markers but leaves the Markdown escapes intact.

The whole-string regex conversions also modify formatting markers inside code: **literal** in a fenced code block becomes *literal*.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions