Form Builder Plugin: an internal link in an email message stops all of the form's emails
Maintainers usually reply within 1 day
@DoctorGuidance is already working on this.
Since Nov 1, 2026.
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
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:
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
pnpm installcp .env.example .envpnpm 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
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from payloadcms/payload
-
Required Lexical field accepts two empty paragraphsPossibly taken @lazerg claimed this 3 days ago. OpenBug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
payloadcms/payload#19222 ·
Maintainers usually reply within 1 day
-
area: ui Bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
payloadcms/payload#19198 ·
Maintainers usually reply within 1 day
-
Grid view: show the file placeholder when a thumbnail image fails to loadPossibly taken @lazerg claimed this 4 days ago. OpenBug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
payloadcms/payload#19181 ·
Maintainers usually reply within 1 day
-
WhereBuilder throws "Cannot read properties of undefined (reading 'value')" when the list URL filters on a field that is not filterablePossibly taken A pull request linked to this issue is open or already merged. OpenBug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
payloadcms/payload#19002 · 1 comment ·
Maintainers usually reply within 1 day
-
Regression: `findByID` with `disableErrors: true` throws `NotFound` for null/empty ids since 3.91.0Possibly taken @Krrishkumar125 claimed this 11 days ago. Openarea: core Bug invalid-reproduction status: needs-triage v3
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
payloadcms/payload#18935 · 2 comments ·
Maintainers usually reply within 1 day
All issues in payloadcms/payload
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
kunchenguid/gh-axi#247 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
lidge-jun/opencodex#8101 · 2 comments ·
Maintainers usually reply within 1 day
-
effort:S P3 refactor
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
nightscout/nocturne#3243 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
JuliusBrussee/caveman#1449 ·
Maintainers usually reply within 1 day