javascripteleventy

How do I handle 429 errors from eleventy-fetch?


I'm making a request with eleventy-fetch like this:

let response = await EleventyFetch(url, {
    duration: "1h",
    type: "json"
  });

However, sometimes it fails with an error:

Bad response for [url] (429): Too Many Requests (via Error)

I know that I'm requesting too often, so I want to build exponential backoff. However, my initial attempt didn't work:

let response;
let attempts = 0;
const maxAttempts = 5;

while (attempts < maxAttempts) {
  try {
    response = await EleventyFetch(url, {
      duration: "1h",
      type: "json"
    });
    break; // If request is successful, exit the loop
  } catch (err) {
    if (err.response && err.response.status === 429) {
      attempts++;
      const waitTime = Math.pow(2, attempts) * 1000; // Exponential backoff
      console.warn(`Rate limit exceeded. Retrying in ${waitTime / 1000} seconds...`);
      await new Promise(resolve => setTimeout(resolve, waitTime));
    } else {
      throw err; // If it's not a 429 error, rethrow the error
    }
  }
}

The error is still being thrown and the exponential backoff never happens. What have I done wrong?


Solution

  • You want to be looking for err.cause, not err.response.

    The source code of eleventy-fetch's RemoteAssetCache.js shows where the error is generated:

    if (!response.ok) {
        throw new Error(
            `Bad response for ${this.displayUrl} (${response.status}): ${response.statusText}`,
            { cause: response },
        );
    }
    

    Here, the response is attached to the error as a property cause.

    To make your exponential backoff work, you just need to switch err.response to err.cause:

    let response;
    let attempts = 0;
    const maxAttempts = 5;
    
    while (attempts < maxAttempts) {
      try {
        response = await EleventyFetch(url, {
          duration: "1h",
          type: "json"
        });
        break; // If request is successful, exit the loop
      } catch (err) {
        if (err.cause && err.cause.status === 429) {
          attempts++;
          const waitTime = Math.pow(2, attempts) * 1000; // Exponential backoff
          console.warn(`Rate limit exceeded. Retrying in ${waitTime / 1000} seconds...`);
          await new Promise(resolve => setTimeout(resolve, waitTime));
        } else {
          throw err; // If it's not a 429 error, rethrow the error
        }
      }
    }