Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ky.retry is pending when response body isn't read #803

@c-arnaud

Description

@c-arnaud

First of all thank you for the awesome library!

Wanted to share something I found when writing some unit tests on one of our integration of the ky library. We implemented a Response hook that is calling a refresh token endpoint when the response from the endpoint has a 401 error code. Inspired by the code from the ky documentation.

  return async (
    request: KyRequest,
    _options: KyOptions,
    response: KyResponse,
  ) => {
    if (
      response.status === 401 &&
      !excludeUrls.some((url) => request.url.includes(url))
    ) {
      const accessToken = await refresh()
      if (accessToken) {
        const headers = new Headers(request.headers)
        headers.set('Authorization', `Bearer ${accessToken}`)
        return ky.retry({
          request: new Request(request, { headers }),
          code: 'TOKEN_REFRESHED',
        })
      }
    }
    return response
  }

It works fine on the browser but when trying to test the feature in a node based environment (SSR, Vitest) it appeared that some of the tests were failing due to a timeout. After a 'bit' of research I found out that it was coming from a specific part of the ky library code where we're trying to cancel the response body stream before retrying the request.

await Promise.all([

Basically in our response hook scenario, the body of the response is never read. And it appears that when it happens, the promise returned by the first cancel call can hang forever.

ky.request.bodyUsed: false
First call to clonedResponse.body?.cancel(): Promise { <pending> }
First call to response.body?.cancel(): Promise { <pending> }
Second call to clonedResponse.body?.cancel(): Promise { undefined }
Second call to  response.body?.cancel(): Promise { undefined }

It can potentially be linked to an undici ticket I found while doing my researches. Either there's a missing clone we do not cancel or something like that: nodejs/undici#4562
It might also be linked to that PR as well: #741

In your unit tests it works because it appears that in all of them, the hook actually reads the body. Not just the headers like what we do in our implementation:

const data = await response.clone().json();

Didn't find any previous report or documentation about this issue so I am not sure about what should be the best approach here. Meanwhile we will try to make sure to read the response body even if that is unnecessary for us to at least make sure that in works in node-based envs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions