К содержимому
Hub
All notes

August 24, 2026 · 3 min read

revalidatePath during render takes down the whole page

Next.jsКэшОтладкаServer Actions

The conversation page was returning a server error. The conversation was created in the database, messages were being stored, but the page could not be opened — the route answered 500 with nothing but an error digest in the body.

The code looked harmless. The page fetched the messages and, along the way, marked them read by calling a server action.

That action, besides writing to the database, called revalidatePath to clear the cache of the conversation list so the unread counter would update.

That was the bug. revalidatePath must not be called during a page render. Next does not treat this as a warning — it takes the entire route down. And what fails is the page, not the line with the revalidation, so the first instinct is to suspect the database queries.

The mistake is easy to make precisely because the call looks like an ordinary function. Nothing at the call site hints that a cache invalidation is hiding inside; you only remember when you open the file.

The fix is to mark messages read with a direct query instead of going through an action. No cache clearing is needed there anyway — the page is dynamic and rebuilt on every request.

The general rule: server actions exist to be called from a form or a click handler, in response to a person doing something — not during page assembly. If you want to call an action from a render, what you almost always want is a plain database query.

And the second half of the lesson: the bug survived a week because nobody was watching the logs. A 500 on a page one person visits a day announces itself to no one. Error reporting went in right after this.

React · TSX
/*  Отметка о прочтении ставится здесь напрямую, а не вызовом  серверного действия markConversationRead.  Действие внутри дёргает revalidatePath, а он во время отрисовки  страницы запрещён — Next роняет весь маршрут с серверной ошибкой.  Снаружи это выглядело как «переписка не открывается»: страница  отдавала 500, и написать было физически некуда.  Сбрасывать кэш здесь и не нужно: страница и так собирается на  каждый запрос.*/if (!user.impersonatedBy) {  await prisma.conversationMember.update({    where: { conversationId_userId: { conversationId: id, userId: user.id } },    data: { lastReadAt: new Date() },  });}