Unhandled request error event (ETIMEDOUT)
Problem
Issue Summary Very rarely, I'm seeing an unhandled error event (terminating the Node process) with the message/code `ETIMEDOUT` being emitted in a piece of code that communicates with the Twilio API. The code in question is calling `users(id).userChannels.list()` in this case, but I don't think the specific endpoint actually matters. The event is emitted by the `request` module used by `twilio-node`. I'm not entirely sure if this is a bug in twilio-node or rather in request, but it seems to me that maybe an event handling function should be attached to the `http` call in `/lib/base/RequestClient.js`, which could catch sporadic error events, and reject the returned promise in those cases. The request docs only mention handling events in the context of streams, but this issue looks somewhat related. Steps to Reproduce Happens once in a blue moon, so not exactly easily reproducible. Exception/Log [code block] (no mention of twilio-node here, but it is the only dependency in this context that uses request) Technical details: twilio-node version: 3.39.3 node version: v12.13.1
Error Output
error event (terminating the Node process) with the message/code `ETIMEDOUT` being emitted in a piece of code that communicates with the Twilio API. The code in question is calling `users(id).userChannels.l
Unverified for your environment
Select your OS to check compatibility.
1 Fix
Implement Error Handling for ETIMEDOUT in Twilio API Calls
The ETIMEDOUT error occurs when a request to the Twilio API exceeds the specified timeout duration, leading to an unhandled error event that terminates the Node process. The underlying issue is that the request module used by twilio-node does not have proper error handling for timeout events, which can result in unhandled promise rejections and process crashes.
Awaiting Verification
Be the first to verify this fix
- 1
Wrap Twilio API Call in Try-Catch
Encapsulate the Twilio API call within a try-catch block to handle any synchronous errors that may occur during the request.
typescripttry { const channels = await twilio.users(id).userChannels.list(); } catch (error) { console.error('Error fetching user channels:', error); // Handle the error appropriately } - 2
Attach Error Event Listener
Modify the request handling in the twilio-node library to attach an error event listener to catch ETIMEDOUT errors. This can be done by extending the RequestClient class to include error handling.
javascriptconst request = require('request'); request.get(url) .on('error', (err) => { if (err.code === 'ETIMEDOUT') { reject(new Error('Request timed out')); // Reject the promise } }); - 3
Set a Custom Timeout
When making the API call, set a custom timeout value that is appropriate for your application's needs. This can help prevent the ETIMEDOUT error by ensuring requests are retried or handled gracefully.
javascriptconst twilioClient = require('twilio')(accountSid, authToken); const options = { timeout: 10000 // Set timeout to 10 seconds }; try { const channels = await twilioClient.users(id).userChannels.list(options); } catch (error) { console.error('Error fetching user channels:', error); } - 4
Implement Retry Logic
Add retry logic for the API call to handle transient errors like ETIMEDOUT. Use a library like 'axios-retry' or implement a simple retry mechanism to attempt the request again after a delay.
javascriptasync function fetchUserChannels() { for (let i = 0; i < 3; i++) { try { return await twilio.users(id).userChannels.list(); } catch (error) { if (i === 2 || error.code !== 'ETIMEDOUT') throw error; await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds before retrying } } }
Validation
To confirm the fix worked, monitor the application logs for any occurrences of the ETIMEDOUT error after implementing the changes. Additionally, perform load testing to simulate high traffic and ensure that the application handles timeouts gracefully without crashing.
Sign in to verify this fix
Environment
Submitted by
Alex Chen
2450 rep