К содержимому
Hub
Все заметки

24 августа 2026 г. · 3 мин чтения

revalidatePath во время отрисовки роняет всю страницу

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

Страница переписки открывалась ошибкой сервера. Разговор в базе создавался, сообщения отправлялись, но открыть переписку было нельзя — маршрут отдавал 500 с одним лишь кодом ошибки в теле.

Код выглядел безобидно. Страница получала список сообщений и заодно ставила отметку «прочитано»:

Функция markConversationRead — обычное серверное действие. Внутри неё, помимо записи в базу, стоял revalidatePath: сбросить кэш списка переписок, чтобы счётчик непрочитанных обновился.

В этом и была ошибка. revalidatePath нельзя вызывать во время отрисовки страницы. Next относится к этому не как к предупреждению: он роняет весь маршрут целиком. Причём падает не то место, где сброс, а страница, поэтому в первый момент кажется, что дело в запросах к базе.

Ошибка неочевидна ровно потому, что вызов выглядит как обычная функция. Слово «действие» в названии ничего не подсказывает — с виду это просто await с записью в базу. А то, что внутри спрятан сброс кэша, вспоминаешь, только когда откроешь файл.

Лечится тем, что отметка ставится прямым запросом, без обёртки в действие. Сбрасывать кэш здесь и не нужно: страница помечена как динамическая и собирается на каждый запрос.

Общее правило: серверные действия предназначены для того, чтобы их вызывали из формы или обработчика нажатия — то есть в ответ на действие человека, а не во время сборки страницы. Если хочется дёрнуть действие из отрисовки, почти всегда нужен просто запрос к базе.

И вторая половина урока. Ошибка прожила неделю, потому что никто не смотрел в журнал: 500 на странице, куда заходит один человек в день, ничем себя не проявляет. После этого случая на сайте появилось слежение за ошибками — оно сообщило бы в ту же минуту.

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() },  });}