#2201·papermark

Revalidate cache button calls a nonexistent route, always fails

Author: MuntahaJabbar12Created Aug 28, 2026Updated Sep 6, 2026

Bug: "Revalidate cache" button calls a route that doesn't exist

File: components/links/links-table.tsx Lines: 525–550 (handler), 1084 (button wiring) Introduced in: 9b8f3c03 — "feat: add multiple brands" (2026-08-21)

What happens

The "Revalidate cache" action on a link row calls:

typescript
fetch(`/api/links/${link.id}/revalidate`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ teamId: currentTeamId }),
})

No route matches /api/links/[id]/revalidate anywhere in the codebase. The only revalidation endpoint that exists is pages/api/revalidate.ts, and it's a completely different shape:

  • GET only — the handler returns 405 for anything else, including the POST this button sends
  • Authenticated by a secret query param checked against process.env.REVALIDATE_TOKEN server-side — not by session/team membership, so the frontend can't call it directly with the current request shape even after fixing the path and method
  • Takes linkId as a query parameter, not a URL path segment

So every click on "Revalidate cache" currently fails with the generic "Failed to revalidate link cache" toast, regardless of team or link.

Reproduction

  1. Open the links table for any document or dataroom
  2. Open the row menu for any link, click Revalidate cache
  3. Toast shows Failed to revalidate link cache every time

Why this wasn't caught

response.ok is false for the 404 the router returns, so the failure path is exercised correctly — the toast fires as designed. The bug is upstream of that: the endpoint being called has never existed under this path, so there's no scenario in which this button succeeds.

Suggested fix

The real /api/revalidate.ts handler is secured by a server-only secret, which this button can't supply from the client. The likely correct fix is a new, session-authenticated route (e.g. POST /api/teams/[teamId]/links/[id]/revalidate) that checks team membership the way the rest of the team-scoped routes do, then internally triggers the same res.revalidate(...) calls that pages/api/revalidate.ts performs for a single link — rather than trying to reuse the existing secret-based endpoint from the client.

Happy to open a PR for this if that direction sounds right — wanted to confirm the intended auth model first, since I don't know if there's a reason the two revalidation paths were meant to stay separate.