Form Builder Plugin: an internal link in an email message stops all of the form's emails

Open Beginner friendly
#19,253 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@DoctorGuidance is already working on this.

Since Nov 1, 2026.

  • #19254 by @DoctorGuidance — open
  • #19258 by @ghazy2001 — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
62/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
postgresql, sqlite, typescript
Domain
backend

Research direction

The failure is in LinkHTMLConverter in packages/plugin-form-builder/src/utilities/lexical/converters/link.ts, where the linked document's numeric ID is passed to replaceDoubleCurlys as href. Start by reproducing with the linked repo test tests/int/form-email-internal-link.int.spec.ts and confirm that a numeric ID throws str.replace is not a function. Done means the email with the internal link is still sent, and the test expects 2 emails and gets 2. Settle what the href should be (a string-safe ID or a resolved URL) with maintainers before choosing the fix.

Written by the indexing model from the issue text.

Description

Bug plugin: form-builder status: needs-triage v3
Describe the Bug

When a form's email message contains an internal link, none of that form's emails are sent on databases with numeric IDs (the SQLite and Postgres defaults). The error is only logged and the submission still succeeds, so nobody finds out. Reproduced on 3.92.1 with SQLite (linked repo) and on 3.80.0 with Postgres.

LinkHTMLConverter takes the linked doc's ID as the href and passes it to replaceDoubleCurlys:

https://github.com/payloadcms/payload/blob/v3.92.1/packages/plugin-form-builder/src/utilities/lexical/converters/link.ts#L21-L28

A numeric ID has no .replace, so this throws. A form's emails are all serialized before any is sent, so one such link stops every email on the form, including emails with no link. The outer catch in sendEmail logs it:

Error while sending one or more emails in form submission id: 2.
TypeError: str.replace is not a function

beforeEmail can't work around this, because it runs after serialization.

With string IDs (tested with idType: 'uuid') nothing throws and the emails are sent, but the link is broken: <a href="4d599d6e-dfb9-4db4-8697-c55321a859ec">.

Discussion #19031 (item 3.5) covers the ID href and proposes a way for Payload to know a document's URL, but it doesn't mention that numeric IDs lose the emails. Converting the ID to a string would keep emails flowing in the meantime, although the link would still be wrong. The replaceDoubleCurlys call on href was added in #4753.

Link to the code that reproduces this issue

https://github.com/sam9191/payload-form-email-link-repro

Reproduction Steps
  1. pnpm install
  2. cp .env.example .env
  3. pnpm test:int

tests/int/form-email-internal-link.int.spec.ts creates a form with two emails, only the second holding a link, then submits it with payload.sendEmail mocked.

  • Custom URL link: both emails are sent and the test passes.
  • Internal link to a page: the test expects 2 emails and gets 0, with the error above. The submission is still created.
Which area(s) are affected?

plugin: form-builder

Environment Info
Binaries:
  Node: 22.22.1
  npm: 10.9.4
  Yarn: N/A
  pnpm: 10.32.1
Relevant Packages:
  payload: 3.92.1
  next: 16.3.3
  @payloadcms/db-sqlite: 3.92.1
  @payloadcms/drizzle: 3.92.1
  @payloadcms/graphql: 3.92.1
  @payloadcms/next/utilities: 3.92.1
  @payloadcms/plugin-form-builder: 3.92.1
  @payloadcms/richtext-lexical: 3.92.1
  @payloadcms/translations: 3.92.1
  @payloadcms/ui/shared: 3.92.1
  react: 19.2.6
  react-dom: 19.2.6
Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 24.6.0: Sun Aug 23 20:30:56 PDT 2026; root:xnu-11417.140.69.712.69~1/RELEASE_X86_64
  Available memory (MB): 32768
  Available CPU cores: 16
Dominant language
TypeScript
Stars
45.6k
Forks
4.4k
Avg merge
1d 15h
Merged PRs (30d)
280

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from payloadcms/payload

All issues in payloadcms/payload

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.