atomic-fetch-retry-infinite-loop.md 1.9 KB

Бесконечный цикл при повторе запроса после refresh-токена

Контекст: В frontend/src/lib/api.ts функция request<T> имела параметр-флаг isRetry, защищающий от повторного входа в ветку обновления токена. А параллельная функция requestFormData<T> (загрузка файлов через multipart) проверяла только res.status === 401 && accessToken, без флага.

Суть: Если после успешного refresh сервер снова отдаёт 401 (токен отозван/невалиден сразу), условие остаётся истинным → новый refresh → повторная загрузка → 401 → … Получается плотный цикл запросов к /auth/refresh и upload-эндпоинту (фактически self-DoS клиента и сервера).

Решение: Прокинуть isRetry в requestFormData, как в request:

async function requestFormData<T>(path, formData, isRetry = false): Promise<T> {
  // ...
  if (res.status === 401 && !isRetry && accessToken) {
    const newToken = await refreshAccessToken()
    accessToken = newToken
    return requestFormData<T>(path, formData, true) // только один повтор
  }
}

Правило: любой код «повтори запрос после обновления токена» обязан иметь однократный guard от рекурсии. Это должно быть единообразно во всех путях запроса (JSON и multipart).

Связанные заметки: [[frontend-api-client]], [[atomic-api-contract-refresh-cookie]] Источник: Code review 2026-06-30 (neyrogovnarik), P1 fix в lib/api.ts

#frontend #typescript #auth #best-practice #bug