payloadcms / payloadcms/payload

Bug: plugin-search silently rolls back the parent publish when a search-doc write fails (swallowed transaction error)

Open
#17,699 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

plugin: search
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Description

Describe the Bug

When @payloadcms/plugin-search syncs a search document during a create/update of an indexed collection, the search-doc write runs inside the parent operation's Mongo transaction, and any failure is logged but not surfaced.

In syncDocAsSearchIndex the search-doc payload.create / payload.update calls are passed the incoming req, so they join the parent document's transaction. The surrounding try/catch calls payload.logger.error(...) and does not rethrow. Because the failed write has already aborted the shared transaction, the parent operation (for example a publish) rolls back — but the caller sees no error: payload.create / the REST / GraphQL request resolves as if it succeeded. The only signal is a server log line, so an editor sees their publish silently revert to the previously published version.

beforeSync runs before the write, so it can validate the shape of the data being synced, but it cannot observe or surface a runtime write failure — a transient Mongo error, a unique-index conflict on the search collection, or a validation error on the search doc.

Source (current main): packages/plugin-search/src/utilities/syncDocAsSearchIndex.ts — each payload.create / payload.update for the search doc is passed req (joining the parent transaction) and wrapped in a try/catch that only logs (e.g. payload.logger.error({ err, msg: 'Error creating ${searchSlug} document.' })) without rethrowing.

Link to the code that reproduces this issue

https://github.com/dawndarkness/payload/blob/repro/plugin-search-swallows-write-errors/test/plugin-search-write-errors/int.spec.ts

Reproduction Steps

The linked branch adds a self-contained integration suite (test/plugin-search-write-errors/) on top of main. It configures plugin-search over a drafts-enabled posts collection and adds a unique: true field (dedupeKey) to the search collection whose value beforeSync sets to a constant, so the second published post triggers a deterministic duplicate-key (E11000) failure on the search-doc write — a genuine write-time error inside the shared transaction.

  1. Check out https://github.com/dawndarkness/payload at repro/plugin-search-swallows-write-errors (or copy test/plugin-search-write-errors/ onto payloadcms/payload@main).
  2. pnpm install
  3. Start the test Mongo: pnpm docker:start mongodb
  4. Run: pnpm test:int plugin-search-write-errors

Observed: the test fails. The server log shows

ERROR: Error syncing search document related to posts with id: '...'
  ValidationError: The following field is invalid: dedupeKey  ("Value must be unique")
  at syncDocAsSearchIndex (packages/plugin-search/src/utilities/syncDocAsSearchIndex.ts:112)
  at hooks.afterChange (packages/plugin-search/src/index.ts:61)

but:

  • payload.create({ ..., _status: 'published' }) for the second post resolves without throwing, and
  • a follow-up payload.find returns only the first post — the second post's publish was silently rolled back.

The assertion encodes the desired contract (the second publish should either surface a visible error, or persist); on current main it does neither.

The same silent revert occurs for any runtime search-doc write failure — a transient Mongo error, or a validation error on a field the search doc does not populate — because all are caught and only logged while the shared transaction is already aborted.

Expected: the parent publish either succeeds, or fails with a visible error returned to the caller.
Actual: the publish silently reverts to the previously published version; no error is shown in the admin UI or returned to the API caller; the only evidence is a server log line.

Which area(s) are affected?

plugin: search

Environment Info
Payload version: reproduces on 3.83.0 and on current `main`
Database adapter: mongodb (the silent rollback is Mongo shared-transaction behaviour)
Reproduction: payload monorepo integration harness (test/plugin-search-write-errors)

Contributor guide

Open the contributing guide

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.

Research direction

Start with packages/plugin-search/src/utilities/syncDocAsSearchIndex.ts and the linked test at test/plugin-search-write-errors/int.spec.ts, then run pnpm test:int plugin-search-write-errors with MongoDB running via pnpm docker:start mongodb. Trace the search-document write failure through the parent transaction and verify that the second publish either returns a visible error or persists successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
mongodb, typescript
Domain
backend, databases, search
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.