Search code examples
node.jses6-promisehttpresponse

Uncaught errors by .catch clause in Promise for JSON HTTP request


I have this function that collects JSON data from an API endpoint:

  export const getDataAPI = (token_id) => {
  const url = `https://api.url.com/tokens/` + token_id;
  const options = {method: 'GET', headers: {Accept: 'application/json'}};
  const request = fetch(url, options)
    .then(response => { if (response.status === 200) {
        return response.json();
      } else if ([404, 429, 500].includes(response.status)) {
        return response.status;
      } else {
        return response.json()
          .then(json => {
            console.log("Error: getDataAPI response status", response.status);
            throw json;
          })
      }
    })
    .catch(error => { throw error; });
  return Promise.resolve(request);
};

I have collected over 1.3M records with this function. I would like to catch any unexpected problems so I can gracefully pause for a few seconds and try again. But I came across 2 errors that were not sent to the .catch clause. Even if response is empty / null / undefined, I would expect at least to see the output of console.log("Error: getDataAPI response status", response.status); These errors only happened once each.

undefined:1

SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at Response.json (file:///home/user/node_modules/node-fetch/src/body.js:149:15)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async file:///home/user/path/to/file.mjs:94:20
file:///home/user/node_modules/node-fetch/src/index.js:108
            reject(new FetchError(`request to ${request.url} failed, reason: ${error.message}`, 'system', error));
                   ^

FetchError: request to https://api.url.com/tokens/29727608 failed, reason: read ECONNRESET
    at ClientRequest.<anonymous> (file:///home/user/node_modules/node-fetch/src/index.js:108:11)
    at ClientRequest.emit (node:events:526:28)
    at TLSSocket.socketErrorListener (node:_http_client:442:9)
    at TLSSocket.emit (node:events:526:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  type: 'system',
  errno: 'ECONNRESET',
  code: 'ECONNRESET',
  erroredSysCall: 'read'
}
  1. I don't understand why these errors were not caught by the function. Can someone please explain this?

  2. How can I fix the function so it also catches this sort of errors?

  3. What is the output I should be looking for to identify these errors, SyntaxError and FetchError? Is there a variable to monitor for error codes for example?


Solution

  • I would expect at least to see the output of console.log("Error: getDataAPI response status", response.status);

    That code isn't in a catch clause, it's in a then clause.

    return response.json()
              .then(json => {
                console.log("Error: getDataAPI response status", response.status);
                throw json;
              })
    
    

    Your catch is simply rethrowing:

    .catch(error => { throw error; });
    

    Which will give you an uncaught error.

    You need to log/handle the error in the catch, not the then clause:

     export const getDataAPI = (token_id) => {
      const url = `https://api.url.com/tokens/` + token_id;
      const options = {method: 'GET', headers: {Accept: 'application/json'}};
      const request = fetch(url, options)
        .then(response => { if (response.status === 200) {
            return response.json();
          } else if ([404, 429, 500].includes(response.status)) {
            return response.status;
          } else {
            console.log("Error: getDataAPI response status", response.status);
            // you can swallow the error here if you want, or try to parse it anyway
            return response.json()
          }
        })
        .catch(error => { 
           console.log('Handling error', error);
           throw error;
        });
    
      // wrapping the request in a Promise.resolve is also unnecessary 
      // return Promise.resolve(request);
      return request;
    };