Ask a Jedi: Handling errors in ColdFusion Ajax applications

Shirzad (cool name!) asks:

How do you prevent the Coldfusion Ajax alert message "error retrieving markup..."? Sometimes errors are inevitable. I want to prevent users on our public site from seeing the javascript alert that pops up whenever there's an error in a coldfusion ajax container (or at the very least change the message so it isn't asking people to add cfdebug to the parameters). Is there any way to do this?

There are a couple ways of handling errors with ColdFusion 8's Ajax features. Here is a quick overview of some of them in regards to the cfdiv tag. First, let's make an error. I'll take a simple cfdiv:

<cfdiv id="testdiv" bind="url:test2.cfm"/>

And here is test2.cfm:

<cfthrow>

As you can guess, this will immediately throw an error when loaded by the div. The cfdiv tag supports an onBindError attribute. It lets you specify a JavaScript function to run when an error occurs (big surprise there). Here is a modified version of our original script:

<script>
function handleError(c,m) {
   console.log('error '+c+' '+m);
}
</script>

<cfdiv id="testdiv" bind="url:test2.cfm" onBindError="handleError"/>

The docs specify that the error handler should take two arguments - a status code and message. When I ran this, I correctly saw the error log, but I also saw the 'naked' CF error in the div. I thought perhaps that the onBindHandler would automatically hide the error but it doesn't. I then tried this:

document.getElementById("testdiv").innerHTML = "Error!";

But for some reason, it wouldn't work. I was able to get this to work:

ColdFusion.navigate('/user.cfm','testdiv');

The file user.cfm was just another random file on my server. You would normally point it to a file that had some kind of 'Error' message.

Another option is to specify a global error handler. This is done with the setGlobalErrorHandler:

function handleGError(s) {
   console.log('global error called '+s);
}   

ColdFusion.setGlobalErrorHandler(handleGError);

Notice though that this function only takes one argument, a message. I don't know why - but it seems to me as if it should work the same as the error error. Anyway, if you use this, and remove the onBindError, the global error handler will fire.

As yet another example of how you can handle errors, the cfajaxproxy tag has an onError attribute. You can also set a specific error handler for an instance of an AjaxProxy object.

Check the docs for other tags and how they can provide support for this as well. (Consider this my "This your homework" part.) CFGRID for example supports onError which works the same as the onBindError for cfdiv.

Comments

I am using CFAjaxProxy with cfc's and when I call a cfc that has an sql syntax error a page returns to the HTTP request that displays the error. Well the returned page does not get handled like an error because it's status code is 200 and the Response Header does not equal server-error. And since it is not a proper response it does not get sent to the callback handler. What I need to do is handle all possible errors, so that the user is not sitting and waiting for a response when none will ever come. Any ideas?

This is how the cfajax.js determines if a response is an error.

$A.isRequestError=function(req){
return ((req.status!=0&&req.status!=200)||req.getResponseHeader("server-error"));
};
# Posted By Michael de la Morena | 9/9/08 1:42 PM
Ah, so you have CF handling errors as well, which means you get a 'pretty' error.

So here is an idea - use onError to handle the errors, and if the error occurs on an ajax request (you can sniff the request), you could just throw a new exception.
# Posted By Raymond Camden | 9/9/08 1:46 PM
Yes, u can use CFHEADER and send a non 200 HTTP Status Code.
# Posted By Henry Ho | 9/9/08 1:54 PM
I have a function that is being called through cfajaxproxy that throws a java.lang.NullPointerException with no other detail. But the interesting thing is it only throws the error when called from cfajaxproxy. Invoke it normally through cfinvoke and it works perfectly. I can see that all the parameters are passed fine as they are logged. If I comment out the error handler in the cfc it throws a generic Status: 500 internal server error. Neither error is much help in figuring out what is wrong. Any suggestions as to how I can determine whats causing it would be appreciated.
# Posted By Paul | 9/9/08 2:24 PM
Don't test it via cfinvoke. Test it via your browser. Ie, use Firebug to see the URL and paste it into a new tab.
# Posted By Raymond Camden | 9/9/08 3:02 PM
Ray: thanks as always for you prompt response!
That took me right to the line of code that was causing the problem. Excellent! Thank you!
# Posted By Paul | 9/9/08 3:35 PM
Ray: Thanks so much for your help on this - much appreciated. One container I'm still having an issue with is the cflayout-tab. Any ideas how to handle errors that give you the javascript alert from in there (other than a cfcatch)?
# Posted By Shirzad | 9/9/08 7:32 PM
Did you try the global error handler I described above? I haven't tried it for a cflayout error, but I'd give it a shot.
# Posted By Raymond Camden | 9/9/08 8:08 PM
Ray: I managed to get the global error handler working for an error in a cfdiv (and even for a cfdiv that's inside a cflayout-tab), but no luck so far with an error in a cflayout-tab. But maybe I'm not doing something right. If anyone can get it to work with a tab, please let me know.
# Posted By Shirzad | 9/10/08 8:53 AM
I got it working. I used the global handler, although cflayoutarea supports onBindError as well. I then gave the tab a name, and was able to use ColdFusion.navigate to send the tab to an error page.
# Posted By Raymond Camden | 9/11/08 9:54 AM
OK - thanks Ray. I'll figure out why my code's not working properly. Again, really appreciate your help on this.
# Posted By Shirzad | 9/13/08 1:09 PM