Skip to content
bminer edited this page Mar 16, 2012 · 4 revisions

Common Pitfalls

A little background

When a call is connected, Twilio sends a request to your server, the twilio-api middleware intercepts this request and emits the 'connected' event for the appropriate Call object. This, in turn, calls the 'connected' event handler(s), which you can use to generate TwiML. Immediately after the event handler(s) have completed, twilio-api will respond to Twilio's request with the TwiML generated on your Call object. By default, if you do not call any TwiML-generating functions on the Call object, twilio-api will respond with an empty TwiML document, causing the call to hangup. In addition, one must take care when using asynchronous function calls within the 'connected' event handler, as the callbacks for these async calls will be executed after the TwiML has already been submitted to Twilio. Because of this, you probably should not generate any TwiML in callbacks of async functions. See below for an example.

Example of what NOT to do:

app.makeCall("+16145555555", "+16145558888", function(err, call) {
    if(err) throw err;
    //Now we can use the call Object to generate TwiML and listen for Call events
    call.on('connected', function(status) {
        //Using an asyncronous function call -- INCORRECT: this will not work as expected!
        fs.readFile('greeting.txt', function(err, data) {
            if(err) throw err;
            call.say(data); //At this point, the 'connected' event handler has already been executed
        });
        /* At this point, no TwiML has been generated yet, and we must serve something to Twilio.
            So, we serve an empty TwiML document.
        */
    });
});

Notice that the 'connected' event completes before the file has been read. This means that Twilio has already requested and received the TwiML for the call. The call will be answered by Twilio and then immediately hung up (since no TwiML is provided).

Note: The above code will work, but your generated TwiML will not be executed until another Call event is triggered.

What to do instead:

app.makeCall("+16145555555", "+16145558888", function(err, call) {
    if(err) throw err;
    //Now we can use the call Object to generate TwiML and listen for Call events
    call.on('connected', function(status) {
        //Put the call on hold
        call.putOnHoldWithoutMusic();
        call.say("This is never executed.");
        //Using an asyncronous function call
        fs.readFile('greeting.txt', function(err, data) {
            if(err) {
                //Probably should terminate the call
                call.liveCb(function() {
                    call.say("Sorry. An error occurred. Goodbye!");
                    //Call will hangup automatically since there is no more TwiML
                });
                throw err;
            }
            call.liveCb(function() {
                call.say(data);
            });
        });
    });
});

Beware: we actually put the call on hold for a little bit here while our server reads the file. Then, we modify the live call by calling the Call.liveCb function. It is important, therefore, to ensure that we handle any errors or exceptions carefully; otherwise, our call could be on hold for quite some time.

This is quite different from Express, which uses the next() callback to provide the ability to make asynchronous calls. Support for this kind of thing may be provided in the future.