Payload Plugins
Pluginspayload-revalidate

Troubleshooting

Fix the common revalidation failures fast — a symptom-first table with the literal dev warnings to grep for, plus the /dev/revalidate map that shows exactly what busts when.

For AI / LLMs: View Markdown

Revalidation is loud when it goes wrong. The plugin logs each failure once per process, in every environment — a production jobs runner silently not revalidating is the one thing it refuses to be quiet about. Every message is prefixed [payload-revalidate], so grep '\[payload-revalidate\]' on your logs surfaces all of them. The literal strings are in the table below.

SymptomCauseFix
Nothing revalidates in a jobs runner or outside a requestrevalidateTag no-ops outside a Next request scope (a console.warn, once). Grep: revalidateTag('…') was a no-op — no Next request scope.Bust from inside a request, or accept that background writes heal on the next request-scoped bust. This is the failure the plugin logs loudest. A CLI seed's end-of-run flush is the one no-op that stays silent — there is no cache in that process to bust, so nothing is wrong (see the seed row below).
Entry materialized untaggedcacheTag failed, so the entry carries no tags and nothing can bust it (a console.error, once). Grep: cacheTag failed — this entry (and likely every entry) is materializing UNTAGGED.Confirm the getter runs inside a 'use cache' scope with cacheComponents enabled.
Bake-in walk skippedThe config was unreachable, so the walk that finds baked-in content couldn't run (a console.warn).The doc still tags by id; only the deep-content walk was skipped. Check the logged warning for the cause.
Hooks don't fire on a plugin's collectionThe plugin ran before the collection existed, so it couldn't hook it. Grep: registered AFTER revalidatePlugin() and NOT hooked for revalidation.Register revalidatePlugin() last in plugins: []. The boot-time warning names anything registered after it. See Quickstart.
List reorders don't refreshThe read depends on a sort or filter field the scope doesn't declare, so a reorder never busts it.Declare every sort and filter field the scope depends on. The /dev/revalidate map warns when a scope is observed but undeclared. See Getters.
Nothing busts during a seedExpected — the payload-seed engine sets context.disableRevalidate on every write.Nothing to fix. The plugin's after-seed listener flushes the seeded surface once at the end: list tags, declared scopes, extraTags, and rule targets for the touched slugs, then all. In a CLI seed that flush quietly no-ops (no request scope, no cache in the process) without the outside-request warning — a fresh dev server reads the seeded data cold anyway.

The three loud failures all log once per process in every environment, production included: revalidateTag no-oping outside a request scope (console.warn), cacheTag failing so an entry materializes untagged (console.error), and the config being unreachable so the bake-in walk is skipped (console.warn).

The /dev/revalidate map

With payload-dev-tools installed, the map renders in four tabs:

  • Explore — the interactive dependency graph (pan/zoom, click a node): editing a node makes its right-hand neighbors stale; amber animated edges are observed bake-ins (the anti-pattern), grey edges are id-references, dashed is richText. Selecting a node answers "what does editing this revalidate?" and "what makes this stale?" plus the observed doc-level reality: which entries carry media:3 right now, own-entry ✓ or baked-in ⚠.
  • Fields — per collection, every field with its computed blast radius: title → doc, publishedAt → doc + list:recent, slug → doc + alias.
  • Reads — every materialized entry with an atomic-health badge (atomic ✓, N baked-in ⚠, undeclared scope!, capped!).
  • Events — the bust log (per-tag reasons) + a manual bust box.

GET /api/revalidate-map serves it all as JSON (dev-gated); POST /api/revalidate-map { "tag": "posts:42" } busts one tag by hand.

The plugin also polices the pattern in dev — a getter baking in populated docs, a cacheIds read carrying an undeclared scope, and docs-with-content passed to cacheIds are all flagged here in the map with the exact field paths to fix. To also print those advisories to the console, opt in with options: { advisories: true } (off by default — they fire per cache-entry materialization and flood dev logs).

Both routes are governed by observe, so they exist only while it is on — the production default registers neither. With observe: true forced on in production, they additionally require an authenticated Payload user.

On this page