I've done more than a few posts recently about error handling and robust exception information, so I thought I'd write up one blog entry that can serve as a nice guide for folks to bookmark. By using "Complete" in the title, I've also virtually assured that I will forget something critical, so please, send suggestions for what I've missed. Let's get started.
What is the point of this guide? Unless you are a perfect code, there is a chance that your ColdFusion application will have errors in it. The question is - what are you doing with your errors? What was the last error that occurred on your site? If I asked you how many errors your site threw yesterday, could you answer with 100% complete accuracy? I'd be willing to bet most people would say no. Shoot, I know I couldn't answer that. So what do we do to help resolve this issue?
The first thing I want you to do is to create an error. Create a new CFM file named errortest.cfm. Insert one line into it:
<cfoutput>#xfhdshsdhj#</cfoutput>
The point of this template is to create an error. Unless you actually have a variable defined with that ugly name, the template will error out. Upload it to your server and run it with your browser.
One of three things will happen:
- You will get an error with the full path shown, like below. This shows that you have not added error management to your application, and that you have Enabled Robust Exception information in your ColdFusion Administrator. Stop reading this blog entry, go to your Admin, and disable it. Now. (Please note that this guide is intended for a production machine environment. Obviously you can keep this setting on in development. I do.)

- You will get an error with no path shown. This is slightly better. It still shows that you haven't added error management to your application though.

- You get an "error page". By "error page" I mean a page saying that an error occurred, but displayed in your standard site layout, or close to it. That's good. You should still continue to read though as I'm going to discuss things that should normally be in an error handler.
Ok, before going on - delete the page you just uploaded. I'd be willing to bet a good 40-50% of us upload test.cfm type files to our servers and forget to delete them. I've never done that. Really.
So - lets talk error handling now. You have two high level options. The first is to set an error template in the ColdFusion administrator:

If you specify a template here, than ColdFusion will run the template when an error occurs. In general though I wouldn't recommend setting the template here.
Your second option - and what I recommend - is to the handle the errors specifically in your application. How? If you are on an older ColdFusion server and only have access to Application.cfm, then you want to the use the CFERROR tag. You can use these tags in Application.cfc as well, but I recommend onError for that. I'm going to cover both though.
A full description of cferror may be found in the docs. I'm going to give you the Ray Camden quickie guide to it. The cferror basically lets you specify an template to run when a type of error occurs. There are 3 main types of errors it monitors: Exception, Request, and Validation. Forget about validation. Don't use it. Exception is the type of error we are most familiar with. It is what I call the 'basic' error. Request is the more serious error. It can occur when your error handler itself screws up. I call this the "Oh S***" error (seriously). It has special restrictions on it I'll discuss in a second. First though, let's look at the syntax you would use in your Application.cfm file:
<cfapplication name="rayrules">
<cferror type="exception" template="error.cfm">
<cferror type="request" template="error_request.cfm">
As you can see - it's pretty simple stuff. I basically said - for the Exception (again, think 'normal' error), run error.cfm. For the Oh Crap error, run error_request.cfm.
If you run any CFM again - you will immediately get an error stating that these files do not exist. You should create blank ones for the time being.
Now for the details. The error.cfm template is a normal CFM page. But it has access to a special variable: ERROR. This variable is a structure that contains a lot of information about the error. What you get there will also depend on the error. SQL errors for example will have different values in the structure than a simple runtime error. Let's just do a quick dump.
<cfdump var="#error#">
If you run your error test again (you may have to reupload it if you deleted it like I suggested, just don't forget to remove it later!), you will see something like this:

I'm not going to list every field - again, check the cferror syntax doc for that. The item you will most care about normal is cferror.message. This is the simplest representation of the error and will be most useful for logging. The diagnostics value gives more detail including a line number which is handy during development.
So what now? Well first off - you probably don't want your public site showing a dump. Let's begin by outputting a nice message to the user. Here is the new version of error.cfm:
We are so sorry. Something went wrong. We are working on it now.
Obviously you can mark that up with nice HTML, use your custom tag layout wrapper, etc. This handles letting the user know something bad happened. At bare minimum, this is better than showing naked errors to the user, but we should do something with the error. I recommend two things.
Log the error. For some odd reason, ColdFusion will nicely log an unhandled error, but will not log a handled error. I believe Blue Dragon logs the error anyway. Since ColdFusion won't log it, we should:
<cflog file="myapperrorlog" text="#error.message# - #error.diagnostics#">
Note that I've specified the message and diagnostics variable. This is a bit of a duplication since diagnostics information will have the same information as message, but I like the shortness of the message value. You could log more than this obviously, but since this is a log file, we don't want to overdue it here.
The next thing we should do is email the error. This is something I've covered before on the blog, so some of you may know my feelings on this already, but what I typically do is email me the entire error structure. I also include other structures so I can see what else was going on:
<cfsavecontent variable="errortext">
<cfoutput>
An error occurred: http://#cgi.server_name##cgi.script_name#?#cgi.query_string#<br />
Time: #dateFormat(now(), "short")# #timeFormat(now(), "short")#<br />
<cfdump var="#error#" label="Error">
<cfdump var="#form#" label="Form">
<cfdump var="#url#" label="URL">
</cfoutput>
</cfsavecontent>
<cfmail to="bugs@myproject.com" from="root@myproject.com" subject="Error: #error.message#" type="html">
#errortext#
</cfmail>
I create my message within a cfsavecontent (more on why in a second), and then mail it. Don't forget the type=html. Now as you can guess, this creates a pretty big email. If you get a 1000 of these, you will be suffering, but consider it incentive to fix the darn bug ASAP. You could also add a dump of the session scope if you wanted, or CGI. Basically, it is better to send more information then you need then to be wanting for more. Having all this detail gives you a better idea of what is going on when the error occurred.
So why the cfsavecontent? One trick I'll often do is to skip the email if I'm currently logged in as an admin on the site. I'll do a quick check, and if I'm an admin, I'll display the error on screen. This lets me see the error more quickly than waiting for an email.
So all together now, here is the error.cfm file:
We are so sorry. Something went wrong. We are working on it now.
<cflog file="myapperrorlog" text="#error.message# - #error.diagnostics#">
<cfsavecontent variable="errortext">
<cfoutput>
An error occurred: http://#cgi.server_name##cgi.script_name#?#cgi.query_string#<br />
Time: #dateFormat(now(), "short")# #timeFormat(now(), "short")#<br />
<cfdump var="#error#" label="Error">
<cfdump var="#form#" label="Form">
<cfdump var="#url#" label="URL">
</cfoutput>
</cfsavecontent>
<cfmail to="bugs@myproject.com" from="root@myproject.com" subject="Error: #error.message#" type="html">
#errortext#
</cfmail>
Now for fun, try modifying your error.cfm. Change the first cfdump tag to a cfpoo tag. If you rerun your template, you will see a blank page. Remember that we created a blank error_request.cfm file earlier? This is what is running now. Basically, ColdFusion has noticed that we had an error, and then our error management had an error, and it's thrown it's hands up in the air and given up. We are now in the request template. The request template has special rules - the most important being - no CFML. That's right - you can't cflog. You can't email the error. You can - however - output error variables. You don't use cfoutput, you just include them. Consider this sample:
This went wrong: #error.diagnostics#
This will display:
This went wrong: Unknown tag: cfpoo. ColdFusion cannot determine how to process the tag cfpoo because the tag is unknown and not in any imported tag libraries. The tag name might be misspelled. The error occurred on line -1.
That's all you can do really. But guess what - I wouldn't do that. Remember - we don't want to reveal any sensitive information to our users, including what caused an error. So what do I recommend?
Go to your site and view source. This will give you the HTML result of one of your pages. Find the content and replace it with a "We're sorry" type message like we used in error.cfm. Then save that HTML. Basically you are creating a static page. This means that if you change your layout, you have to regenerate your error_request.cfm page. Of course, you could just not use any layout at all, but most people want their pages to have a standard look and feel.
Unfortunately, nothing is logged when this error happens. So what can you do? One thing to consider is checking the web server log files to see when the file is run. If you see it running often, then double check your error.cfm file for possible errors. If worse comes to worse, temporarily wrap your error.cfm itself in a try/catch and see what shows up when you dump cfcatch. I'm open to suggestions here - but there is a reason they (ok, I) call this the "Oh Crap" error.
Ok, so I've covered quite a bit of information here, but it applies to Application.cfm and CFERROR. What if you are using Application.cfc? Well one thing to remember is that you can just as easily put CFERROR tags inside your Application.cfc file. That is allowed, and I've done that before. But what if you want to use the onError method?
In general - a lot of what I said about the Exception type for CFERROR applies here. You want to present a nice message to your user. You want to log the error. You want to email the error to yourself. But there are some subtle differences.
Consider this very simple onError:
<cffunction name="onError" returnType="void" output="true">
<cfargument name="exception" required="true">
<cfargument name="eventname" type="string" required="true">
<cfdump var="#arguments#"><cfabort>
</cffunction>
All I've done here is dump all the arguments sent in. Now go back to your error file (the one you made to throw errors) and change it to this:
<h1>Hellow World</h1>
<cfoutput>#xfhdshsdhj#</cfoutput>
Run it in your browser, and you will see this:

Notice that the HTML before the error is displayed in the browser. If we had used a nice error message instead of the dump, the user would see both. This can result in oddly formatted pages. What you can do instead is simply handle the error and cflocate to the a nicer page:
<cffunction name="onError" returnType="void" output="true">
<cfargument name="exception" required="true">
<cfargument name="eventname" type="string" required="true">
<cfset var errortext = "">
<cflog file="myapperrorlog" text="#arguments.exception.message#">
<cfsavecontent variable="errortext">
<cfoutput>
An error occurred: http://#cgi.server_name##cgi.script_name#?#cgi.query_string#<br />
Time: #dateFormat(now(), "short")# #timeFormat(now(), "short")#<br />
<cfdump var="#arguments.exception#" label="Error">
<cfdump var="#form#" label="Form">
<cfdump var="#url#" label="URL">
</cfoutput>
</cfsavecontent>
<cfmail to="bugs@myproject.com" from="root@myproject.com" subject="Error: #arguments.exception.message#" type="html">
#errortext#
</cfmail>
<cflocation url="error.cfm">
</cffunction>
All I did was take the code from my original error.cfm file and place it in here. The Exception argument here looks a bit different. No diagnostics key. So I just logged the message for now. My error.cfm file now only contains the message:
We are so sorry. Something went wrong. We are working on it now.
Lets recap:
- Do a quick test to figure out how your application responds to errors.
- If robust exeception information is displayed, turn it off.
- Use CFERROR or onError to handle errors.
- Do your own logging, and email yourself a detailed report.
I hope you find this guide useful, and please let me know how I can improve it.
Archived Comments
Ray--
Thanks so much for taking the time to post this. I have been looking for something exactly like this for a while now, and it has helped to provide a context for attacking the error-handling issue that is still daunting for a relatively new CF developer such as myself.
Thanks for all that you do!
For those of us using CF for Flex apps...
...do these two dumps need to be prefaced with "StructKeyExists":
<cfdump var="#form#" label="Form">
<cfdump var="#url#" label="URL">
?
Of course if you have an error in your onError() the same thing will happen with your 'exception' error in the Application.cfm example.
In which case you could use either methods shown above, those being a global error template in CF Admin or a 'request/oh crap' cferror in onRequestStart()
Nice post Ray
Ray - Thanks for this very complete post about error handling. I used this type of process throughout my websites and it has help me find many "bugs" once I release an appliation to production.
I've been using cfdump in the email that goes out when an error occurs as you illustrate in your Application.cfc code above.
One issue that has always bothered me (mainly because I'm AR) is the extra output in the email that cfdump causes. All the text for the javascript functions that control toggling just get dumped into the email's body. All I want to have in my email body are the contents of the structures (Exception, Form, Session, CGI, etc).
I've tried using cfloop with collection = but the problem is that some of the structures contain complex elements (arrays, another structure) as values so the cfloop collection = fails.
This cfdump issue actually caused me a problem with one of my email clients. That POP3 email client would not display the output from cfdump because with the extra javascript code text outputted the HTML for the email body was not well formed. So I got just part of the error message and no data from the structures.
So if anyone has a good replacement for cfdump to output the complete contents of the structures (Exception, Form, Session, CGI) in a manner that will display correctly in an HTML formatted email, I'd love to hear about it.
Bruce, have you ever tried this:
<cfdump var="#application#" format="text">
Specifically, format=.
Nice post. For our production servers, we do something very similar, but add a couple twists. First, all messages get sent to a common e-mail address (errors@yourcompany.com), which through filters on the mail server we can send a copy to the developer(s) responsible for that site. Those same messages also get pulled into a database (via a scheduled script). The messages can then be searched by site because we include the domain name in the subject line. On the error page itself, we include a reference number - I believe it's the first part of a UUID. Then if a customer happens to get an error, we can get them to tell us that reference code and (using our error database, which has a front-end) we can pull up their error. Overall, we've used this for a few months now, and it's been very helpful.
I looked up the attributes for cfdump and the default "format" is listed as 'text'. However, that's not the case.
<!--- outputs as text--->
<cfdump var="#cgi#" format="text">
vs
<!--- outputs as HTML --->
<cfdump var="#cgi#">
I posted a bug with Adobe.
The "format" attribute should be a killer way to get detailed errors on the mobile phone (at last!).
Ray and others - I'd love to use cfdump's format attribute but unfortunately we've not yet moved our production servers to ColdFusion 8, they are still using CF 7.
Thanks, this post it's very clear and complete.
We do something similar on our production server and this is very useful to notify us errors in real time and to provide a better look & feel to error pages.
Ray,
Great article, thanks. I wanted to print it but found no good way to do so. Do you have a printer-friendly feature that is not turned on? Would be awesome to be able to print these in a printer-friendly way.
k2
I disabled printing when I went to the new design. I was having issues w/ search engines running the print form and running up the RAM on the box.
You can download the PDF now via the download link above.
@David Buhler
It looks like the format option is supposed to be used with the output attribute. That is when it defaults to text.
@Bruce
I imagine you could write a more complex error.cfm template and then just do a test on each collection item in your cfloop to determine its type and then handle the output appropriatly via the isSimpleValue(), isArray(), isStruct(), isXML() functions.
CoolJJ
Ray,
Nice and helpful post. If i implement it on MX7 the whole mail is visible on the page and all cf code shows on view source?
what am i missing?
@CoolJJ - Format can be used w/o output, it just defaults to output=html.
@Franz - so if you remember, I mentioned there were two types of errors. Exception=You can use CFML, Request=You Can't. Did you only use the Request one? Remember to use 2 different templates.
Ray,
I use
<cferror type="exception" template="error.cfm">
<cferror type="request" template="error_request.html">
the x.cfm is online if you want to check. Look at the <cfset x = 1> in the page source.
Thanks franz
I went to your web site (hey, how about a warning next time ;), went to x.cfm, and ended up on anice error page w/ no CFML code in it.
Ray,
I changed it back. It shows only on my live but not on the development server. Witch cf server setting could cause it?
Thanks
Thanks much for the PDF.
K2
yep... another thanks for the PDF!
Not quite sure I get you Franz. Again, if you used 2 cferror tags, ensure the templates are pointing right. The Exception one points to the template w/ CFML in it. The request one points to the one with no cfml in it.
I'm implementing your CFC method above. But, how do I trap and handle Request and validation errors in this way without using cferror? Is there a way or am I missing something?
In order to handle an oh crap error when using onError, I believe you would want to add a cferror tag,type=request. Normal errors should go through onError still.
Try this out. :)
Ray,
That's what i have. The first 2 lines of application.cfm are:
<cferror type="exception" template="error.cfm">
<cferror type="request" template="error_request.html">
And when you hit x.cfm, you end up on error.cfm? With CFML showing up?
Ray,
How do I go about creating a Request or Validation error to test this.
k2: A request error is the Oh Crap error. You can force this by making your exception error itself throw an error.
Don't worry about validation errors. They are used when you use CF's built in form checking, which no one uses, and no one should use.
Thanks. I got this working, but with one problem. My Request Error page displays #error.message# instead of the actual error. I just found out that #error.message# is not available on a request error. Thanks CFQuickDocs.
The function:
<code>
<cffunction name="onError" returntype="void" output="true">
<cfargument name="Exception" required="true">
<cfargument name="EventName" type="string" required="true">
<cferror type="request" template="error/RequestErrorDetail.cfm">
<cfinclude template="error/ErrorDetail.cfm">
</cffunction>
</code>
This does work, but I find it much more useful to error trap the exception error page this way.
<code>
<cffunction name="onError" returntype="void" output="true">
<cfargument name="Exception" required="true">
<cfargument name="EventName" type="string" required="true">
<cftry>
<cfinclude template="error/ErrorDetail.cfm">
<cfcatch type="any">
<cfdump var="cfcatch">
</cfcatch>
</cftry>
</cffunction>
</code>
This way I can email the request error (or any other error the 'error' page generates) using the cfcatch scope or do whatever I want with it. Is there a problem doing it this way?
@Ray:
I'm still a bit cloudy on how to incorporate handling your "oh, crap" error into the onError in Application.cfc. Do you have a quick version of the onError function that you can post to show how you handle both?
Try adding <cferror type="request" template="something.cfm"> to your onRequestStart.
One of my applications makes heavy use of CFDIV, ColdFusion.navigate, etc to update the "content" cfdiv only. cflocation doesn't work well in that situation and when I try to cfinclude I just get a blank screen instead of my friendly error message and links to get back. I tried to just use cfset ColdFusion.navigate('/errors/error.cfm','content') but application.cfc onerror function doesn't know what to do with that. can you help straighten out my thinking?
Hi Ray,
I have implemented the error handling code in my Application.cfc using the onError() method and all works fine except for the <cferror> tag in the onRequestStart() method to trap the request error.
I have included my Application.cfc for reference:
<cfcomponent output="false">
<cfset this.name = "mytestportal">
<cfset this.applicationTimeout = createTimeSpan(0,0,20,0)>
<cfset this.clientManagement = true>
<cfset this.clientStorage = "cookie">
<cfset this.loginStorage = "session">
<cfset this.sessionManagement = true>
<cfset this.sessionTimeout = createTimeSpan(0,0,20,0)>
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfreturn true>
</cffunction>
<cffunction name="onApplicationEnd" returnType="void" output="false">
<cfargument name="applicationScope" required="true">
</cffunction>
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cferror type="request" template="dspErrorRequest.cfm">
<cfreturn true>
</cffunction>
<cffunction name="onRequest" returnType="void">
<cfargument name="thePage" type="string" required="true">
<cfinclude template="#arguments.thePage#">
</cffunction>
<cffunction name="onRequestEnd" returnType="void" output="true">
<cfargument name="thePage" type="string" required="true">
</cffunction>
<cffunction name="onError" returnType="void" output="false">
<cfargument name="exception" required="true">
<cfargument name="eventname" type="string" required="true">
<cfset var errortext = "" />
<cfset var mailToAddr = "developersEmail@developers.com" />
<cfset var mailFromAddr = "applicationEmail@appDomain.com" />
<!---
IF DEVELOPMENT, SIMPLY USE THIS...NO NEED TO SEND EMAILS!
<cfdump var="#arguments#"/><cfabort/>
--->
<cflog file="#this.name#" text="#ARGUMENTS.exception.message# - #ARGUMENTS.exception.RootCause.Message#" />
<!--- BUILD THE EMAIL CONTENT --->
<cfsavecontent variable="errorText">
<cfoutput>
An error occurred: http://#cgi.server_name##cgi.script_name#?#cgi.query_string#<br />
Time: #dateFormat(now(), "short")# #timeFormat(now(), "short")#<br />
<cfdump var="#ARGUMENTS.exception#" label="Error">
<cfdump var="#FORM#" label="FORM">
<cfdump var="#URL#" label="URL">
<cfdump var="#SESSION#" label="SESSION">
<cfdump var="#CGI#" label="CGI">
</cfoutput>
</cfsavecontent>
<!--- E-MAIL THE ERROR TO THE DEVELOPER(S) --->
<cfmail to="#mailToAddr#" from="#mailFromAddr#" subject="Error: #ARGUMENTS.exception.message#" type="html">
#errorText#
</cfmail>
<cflocation url="dspError.cfm">
</cffunction>
<cffunction name="onSessionStart" returnType="void" output="false">
</cffunction>
<cffunction name="onSessionEnd" returnType="void" output="false">
<cfargument name="sessionScope" type="struct" required="true">
<cfargument name="appScope" type="struct" required="false">
</cffunction>
</cfcomponent>
When I added an invalid CFML tag in my error handler page:
<cfpoo >
kjhgjhg
</cfpoo>
I simply get the standard coldfusion error message:
A tag starting with 'CF' has been detected. This tag is not supported by this version of ColdFusion. Please verify your typo and try again.
Is there something I'm doing wrong or can you confirm the exact method for trapping these coldfusion errors, if possible?
Thanks!
cfinclude seems to work if I change the output attribute to true on the onError function. but if output is false, i get nothing. That only confuses me because I have a cfinclude for a login form in the onRequestStart method and that worked. I thought I never put output=true on any of my cfc methods but I looked again and the onRequestStart method was also output=true. problem solved?
output=false is indeed generally wanted. I wouldn't use it. You mentioned when you made use of cflocation, stuff in cfdiv, etc, didn't work. That isn't surprising if the error is in ajax. I'll have to test this. One possible idea - make all your ajax requests go to urls with ajax_ in them. If you see that the error is here, then use cfinclude as opposed to cflocation.
what I mean by cflocation not working is that you can't direct the output to a cfdiv, it has to replace the whole page, right? with a ColdFusion.navigate() I could put the error message in the 'content' cfdiv (so I tried all sorts of ways to force a ColdFusion.navigate and couldn't do it. I didn't see any Event on cfmail I could use to fire it off either.
Let me make sure I'm reading you right. You are talking about how to handle an error when the error occurs inside something loaded by cfdiv/cfwindow/cfpod, right?
in my application there is an index.cfm and EVERYTHING is loaded into a cfdiv or cfwindow. when there is an error my first thought was to send the friendly error to the 'content' cfdiv but opening up a new cfwindow would work as well, maybe better.
Hmm. That seems a bit.... odd. But I'll ignore that. So I can see how cflocation won't work in the result of a cfdiv/cfwindow. In that case you want to either cfinclude or cfoutput a result of some sort. The issue though is that you could possibly still have partial content, which is why I suggested cflocation in general. Ie, partial content then your error message. You know, CF's Ajax support has a setGlobalErrorHandler function. You could try that. In that case, you don't handle the error, but let it occur, and then use the JS function to handle it. You lose nice email reports though. You may want to try using onError, mail, log, and then cfthrow to throw a new error which the JS function would trap.
This is such an important post for a lot of people, I got you an item off your wishlist (80s music CD) in appreciation
Great information.
Thank you Ray.
See you at Sakura. :)
Great Article Ray!!
One thing that we have been doing is to clean up the error text into a simple readable email and log format by parsing the error structure, there is still an option to dump if the need is there (our error handler is a component that exists in the application layer and is called from the application.cfc). But normally you can get what you need through a little parsing which I find much easier to digest when coding.
Another thing that we do is check to make sure that things exist before we attempt to email them, for instance I have run into errors where parts of the session simply do not exist or even some of the application layer components, so I've learned to put a isdefined() check on almost anything that get's parsed. If it's not there then it's simply not provided, it's more work but in liue of a huge message it's very useful for us.
Thanks
-Paul-
Paul - if you see my comment above, I'm looking for a good parsing function instead of using cfdump. So if could share your parsing function code that would be great.
Thanks for the post Ray!
Do you know of a way to access the (optional) errorcode and (custom) type cfthrow args from a (CFERROR) error handling page.
It looks like errorcode and type are only available within a cfcatch block and do not bubble up to the cferror page.
Cheers
David
If they don't exist, then I'm not sure what to recommend. I always thought the complete error object was available.
Cheers Ray
On further investigation, I didn't look hard enough.
Within a CFCATCH the errorcode etc is available (top-level) within the cfcatch struct. Once passed to a cferror template the error object is no longer in the top-level of the error struct but available at error.RootCause.
What started to trick me is cfcatch.type will report a custom error type but error.type (even when a custom error is specified) will always be 'coldfusion.runtime.CfErrorWrapper'.
(I'm using CF8,0,0,176276 Enterprise)
Cheers
David
I have the same question as posted by Niall above. That is, I have added
<cferror type="request" template="error_request.cfm"> to the onRequestStart() function in Application.cfc but it doesn't seem to catch the error and display the custom template. I still get the standard coldfusion error message. Any help would be greatly appreciated. Thanks!
Great post for error handling, but what about missing template handling? This code does not appear to handle the standard CF error when a page that does not exist is called by the user. Am I doing something wrong?
I'd just use the onMissingTemplate support in CF8.
Ray,
Unfortunately we are still running CF7 and plan to do so for a few more months. What is your solution for missing template handling on CF7, cos the setting in CF Administrator simply does not work on boxes hosting more than one site!
Thanks,
Steve
I don't have a great answer. You can use IIS's check for script existence setting and then handle the 404 that way, but not sure if a solution exists for Apache.
Thanks, this code is very helpful.
@Shama
I have implemented a solution for the complete exception and request error handling - just haven't got round to posting it.
If you don't mind - and Ray doesn't mind me using this post for this - you can mail me directly for the source code to n.odoherty [at] niallodoherty [dot] com
These are in no way complete - but were intended to be used as a starting point for implemnting in new applications. Therefore there will be "some" changes required.
Niall, non-commercial offers like these are fine with me.
the error on line -1 error can be fixed here
http://kb.adobe.com/selfser...
I thouhgt I will pose this question here as it is related to error handling. My company recently moved a site which was running on ColdFusion 5 and SQL 2000 to new servers (CF 8 and SQL 2005). A new error handling method was also introduced. Now, I understand this is not the recommended way of error handling, but it was something that was thrown in at the last minute. This method works by adding a Cftry and cfcatch pair around all requests - this code is added to the index page which includes all the other pages. The cfcatch will write to database and email us and also display a friendly error message to the user.
Now, with the new servers we started to have severe performance issues and to top that some data loss also was happening. It appeared as if data was rolled back. Today, we turned off the error handling and the servers are working like a charm now.
Now, we are not entirely sure if the error handling caused all the problems because we were also trying other things like rebuilding indexes for database tables. Anyone had similar issues? Can cftry cause this kind of data rollbacks?
Not sure about the rollbacks, but i know that using try/catch like that is DEFINITELY not recommended, and I'd be willing to bet is that it is definitely the cause of your performance issues.
You do know that CF5 does support cferror, right? Why not just use that?
Ray,
I love to have try/catch blocks on my error page as well, specifically early on.
There is not much worse than throwing an error on your error page while attempting to handle the email; logging for an error.
Dear user, while we were handling a system error another internal error occured, both have been noted
How does the CFCOMPILE utility interact with CFERROR? (Still on CFMX7 right now.)
Quote from the MX7 LiveDocs: "To ensure that error pages display successfully, avoid using the CFENCODE utility to encode pages that include the CFERROR tag."
OK, I'm concerned about CFCOMPILE with deploy option, not the the old cfencode, and I'm specifying my CFERROR tags in Application.cfc, onRequestStart. Also integrating this with sitewide error management using onError and CFCATCH. So I'm really wondering if a sitewide error management strategy can be impacted by the use of compile. Thank you!
Also, just for clarification: Am I correct in thinking that CFTHROW or CFTHROW will stop execution in it's tracks and there would be no need for a CFABORT in that context? Example without a throw: CFCATCH output something CFABORT /CFCATCH... Of course we would want to use the abort to stop further execution, if that is desired.
Alternative Example: CFCATCH throw or rethrow right here /CFCATCH... In this alternative example, am I correct in thinking that the CFABORT would be unnecessary to halt execution past the CFCATCH, since the CFTRHOW or CFRETHROW would stop execution here and redirect to the error management routines?
Thank you for your post and advice.
great examples, great tutorial!
Hi:
I'm working on database conversions with several queries using ColdFusion.
I'm outputting the status of each group of queries when they begin and end. I have been hard coding the line numbers and would like to know if there is some CF variable that I can use to output the line numbers dynamically.
The problem is when I add code to the pages, the lines increase or decrease so the line numbers get screwed up.
I tried creating include files, but sometimes that's more work than it's worth.
Any ideas?
Thanks!
This may be a bit silly - but why not just remove the line #s? If you are outputting the result of query GetFoo, a user could simply open the CFM and do a quick ctrl-f to find the query. That seems a heck of a lot easier than worrying about the line numbers.
The reason we're putting in line numbers is being the page has over 8000 lines of codes and queries. The queries look pretty much the same and the only thing that shows when the queries run is this:
I have this at the top of the page:
<cfflush interval="10">
<cfset nowtime = #timeformat(now(),"hh:mm:ss")#>
Then I have the elapsed time calculated in an include file
Then the hard coded line number is output to the screen with the elapsed time so we know how long each query takes to run.
The line numbers are so we can easily find the queries in the file. If the page halts for server time outs or failures, we can quickly get to that location and examine it.
What is "GetFoo"?
Um, not to offend, but if your file has 8K lines of code, it is in serious bad shape. It needs to be rebuilt. There is no hard and fast rule as to how big a file should be - and I've been guilty of large files as well (2k for blog.cfc), but 8k certainly falls into the -way too big- category. Your problem is not the line #s, but the file itsef.
getFoo was simply an example.
My request and exception error types seems to be reversed. Rather, I am getting a request error for all errors (I forced an error using <cfoutput>#nosuchvariable#</cfoutput>). I am using CF 5. I have the two lines:
<cferror type="exception" template="errorException.cfm">
<cferror type="request" template="errorRequest.cfm">
in my application.cfm. On a basic error (like the one above), the page that gets called is the errorRequest.cfm and of course no cfml gets executed. Is there any setting in my server that would cause this?
Thanks!
@xavy - I think I recommended this on this entry somewhere - I may not have - actually I didn't - I recommended it in another blog entry. Anyway, if you find your desired error handler NOT running, it typically means an error in your error handler. Edit errorException.cfm to remove ALL code except "Ray Rocks". If that works, then look at the code you had, and find the problem.
Now, I feel stupid. And Ray really rocks!
Thanks!
Can you access individual errors from a form that has multiple errors? All I get is a list of <li>s with no way to access which is which. ColdFusion doesn't seem to provide a struct with the error and the message as a key pair.
ASP.NET would handle this without a blink.
Michael, what do you mean by multiple errors? An error, by it's very nature, stops execution. CF gives you full access to the exception. You can have multiple errors on one page if you use try/catch, and in each catch you have access to the full exception as well.
CF handles this w/o a blink too. ;) Unless I'm misreading you somehow.
Maybe this was answered before.
When logging or emailing the error I grab the relevant parts of the "error" structure.
On SQL errors I use error.RootCause.sql which reports the offending SQL. The problem is when using cfqueryparam the parameters are shown as (param 1)...(param X). How can I get the values of the parameters?
This is what I found in this regard: http://www.mail-archive.com... but as you can notice is way too complicated.
Thank you
EstebanD
Well it is going to be a bit complicated no matter what. You will need to get the args and replace each ? with the proper arg. I'm not sure that data is available in the error struct. You may have to dump it to see.
I know this is probably already been answered but.....
I have a customer who has an older system, meaning it was coded in CF4
He used a LOT of <INPUT TYPE="HIDDEN" NAME="FIELD_REQUIRED"
VALUE="YOU MUST SELECT A VALUE">
instead of using client side validation.
He came to me a few months ago wanting to upgrade to CF7.
I knew these among many other lazy coding techniques would bit me in the but, and i urged my customer to fix all this prior to the upgrade.
So know he has a site with 1500 documents that have 3000 some odd hidden fields that need to be replaced.
When i setup his App.cfc, i used some generic error catching and just shows an Opps page.
Problem is, those hidden required fields cause a CFTHROW which in turns stops processing and shows the Opps page.
I am now tasked with letting these errors slide through the onError() event. But how?
Is there a certain type of error this produces?
Anyway, thought I would add this to the comments about Error Handling. DONT USE HIDDEN REQUIRED FIELDS. they suck!!
Tim
Update on my issue..
turns out this will catch those errors in the onError() event
<cfif Arguments.Exception.message NEQ "Form entries incomplete or invalid.">
<cfoutput>#Arguments.Exception.detail#</cfoutput>
</cfif>
This still does not solve my issue. Seems like you could put a try on the form page to catch the error, wrong. its caught by App.cfc before it hits the form action page.
So ignoring this error seems impossible at this point.
Will update if I find a solution.
Tim
sorry,
This should have EQ, not NEQ
<cfif Arguments.Exception.message EQ "Form entries incomplete or invalid.">
<cfoutput>#Arguments.Exception.detail#</cfoutput>
Fill out the form dummy :D
</cfif>
Hi Ray,
Thanks for yet another great article.
I'm using CF7 and whenever I try and log
#arguments.eventname# and #arguments.exception.message#
I get messages like this
"Error","jrpp-738","12/15/08","12:02:33","HRA_V2","Event Name: onRequest"
"Error","jrpp-738","12/15/08","12:02:33","HRA_V2","Message: Event Handler Exception."
I have found I need to look to the "Root Cause" to actually see the information you are mentioning:
<cflog file="#This.Name#" type="error" text="Template: #arguments.exception.RootCause.TagContext[1].template#">
<cflog file="#This.Name#" type="error" text="Line: #arguments.exception.RootCause.TagContext[1].line#">
<cflog file="#This.Name#" type="error" text="Message: #arguments.exception.RootCause.message#">
This produces:
"Error","jrpp-1603","12/19/08","09:18:21","HRA_V2","Template: /www/html/root/throw-error.cfm"
"Error","jrpp-1603","12/19/08","09:18:21","HRA_V2","Line: 1"
"Error","jrpp-1603","12/19/08","09:18:21","HRA_V2","Message: Variable XFHDSHSDHJ is undefined."
Curiously if I put <cfthrow object="#arguments.exception#"> in the onError function I see the real error (eg. Variable XFHDSHSDHJ is undefined), but if I <cfdump> it, it is wrapped in the "Event Handler Exception".
Any ideas? I'm confused as to why this happens to me but not anyone else? ;-)
Good point on the logging there. I'm not quite sure what your second point is. When you cfdump the error, you are seeing a data-centric view of the exception. Ie, what went wrong. When you let an error flow out naturally, CF will take the exception and display it. You can actually find the exception.cfm file and edit it. It basically does the same thing we do in error handling - inspect the exception and print out the important information.
Thanks Ray - I think the reason you couldn't understand my second point is that because until now I wasn't aware that the exception.cfm existed and was doing all that for us. :-)
Is there any problem with doing this:
arguments.exception.RootCause.TagContext[1].template
Will the exception passed into the onError() method of Application.cfc always have a RootCause, with a TagContext array of at least 1 element?
I'm not sure it always will have it - but you can always check:
if structkeyexists(exception, "rootcause") and structkeyexists(exception.rootcause, "tagcontext") and arraylen(exception.rootcaue.tagcontext gte 1
What if I don't want to wait for the email to arrive and I am not logged in as admin, can I create a simple way to turn of onError?
You could check for the remote IP and say, if it is in a certain safe list, then reveal the error. Really depends on how simple you want it.
Great idea! That is an easy one to do - Thanks!
Just thought I would shared my onerror function. When I am at my desktop in my office and the IP matches I get the full error and the email sent to my Iphone gets the small version of the error - while everyone else sees a nicely formatted error:
<cffunction name="onError" returnType="void" output="true">
<cfargument name="exception" required="true">
<cfargument name="eventname" type="string" required="true">
<cfset var errortextemailonly = "">
<cfset var errortext = "">
<!--- plain error message email for IPhone --->
<cfsavecontent variable="errortextemailonly">
<cfoutput>
Error on Page: http://#cgi.server_name##cgi.script_name#/ param: #cgi.query_string#<br>
<br>
Refferal Page: #cgi.http_referer# <br>
<br>
User's IP: #cgi.REMOTE_ADDR#<br>
<br>
Server Port: #cgi.SERVER_PORT#<br>
<br>
Error Date: #dateFormat(now(), "short")# #timeFormat(now(), "short")#<br>
<br>
User's Browser: #cgi.HTTP_USER_AGENT#<br>
<br>
------------------------------------<br>
CF Error: #arguments.exception.message#<br>
</cfoutput>
</cfsavecontent>
<!--- full error message for desktop troubleshooting --->
<cfsavecontent variable="errortext">
<cfoutput>
#errortextemailonly#<br>
<cfdump var="#arguments.exception#" label="Error">
<cfdump var="#form#" label="Form">
<cfdump var="#url#" label="URL">
</cfoutput>
</cfsavecontent>
<cfif Application.emailErrorMessaging EQ "on">
<cfmail
to="dave@#Application.widgetURL#"
from="#Application.coname# <dave@#Application.widgetURL#>"
subject="Error #Application.coname# #arguments.exception.message#"
type="html"
server="#Application.mailServer#">
#errortextemailonly#
</cfmail>
</cfif>
<cfif #REMOTE_ADDR# NEQ #Application.davesIP#>
<!--- Show visitors the nicely formatted error --->
<cflocation url="exceptions/error_exception.cfm" addtoken="no">
<!--- Show me the full error for the desktop --->
<cfelse>
<cfoutput>#errortext#</cfoutput>
</cfif>
</cffunction>
Hi Ray,
We have on missing template handler in CF7 and CF8 and we can call in the Applicaiton.cfc file.But how can we handle the missing template error in CF earlier versions without setting up in ColdFusion administrator.
I believe the only way to do it is with your 404 handler in the web server. In IIS you have to also use 'check for script existence' in order for it to work. I don't think I ever got it working in Apache though.
the problem with using the webserver is you will loose all your form and request scope vars.
it will work in IIS as Ray points out, also in Apache as well, but you can forgot it if you want to do url rewrite with either of these webservers
Well, form variables shouldn't be posted to a 404, should it? ;)
ideally no, but if you wanted to use that technique to do url rewrites with the older versions of CF, then those are the issues you will face when using a custom 404 file with a web server.
the web server itself with do an internal redirect to the 404 page.
for plain error catching using a custom 404, it will work just fine.
I would not recommend using a 404 for url rewriting. Apache has that built in 'for real' and IIS offers it as well.
Have a question that my boss wants answered. We have a variety of sites that we run, some using onError() and Application.cfc; and some using CFERROR and Application.cfm. I cannot post code from the sites here (not allowed), but I can tell you this much: in Application.cfm we have CFERROR calls from request, exception, and validation. In Application.cfc, the onError() has handling built in for validation, and all other errors get processed in the same way.
The error handlers work, we get enough usage out of them to know that. There are times though that an error is not caught by our handlers, and gets caught by the site wide error handler.
My boss doesn't believe any error should ever make it that far, and all errors need to get caught and handled by our error handlers regardless of their cause. I believe he is being unrealistic about that, that there are some errors that just cannot be trapped beforehand and that is why the site wide error handler is there. But I need something to back up my thoughts.
I can give you a couple of examples of error messages the site wide has caught, if that is any help.
ERROR DIAGNOSTICS:String index out of range: -1 null <br>The error occurred on line -1. error.message = String index out of range: -1 error.rootCause = java.lang.StringIndexOutOfBoundsException: String index out of range: -1
ERROR DIAGNOSTICS:Session is invalid null <br>The error occurred on line 79. error.message = Session is invalid error.rootCause = java.lang.IllegalStateException: Session is invalid
ERROR DIAGNOSTICS:Operation failed on the data source named ""FREE"". Reason of failure ""java.sql.SQLException: [Macromedia][SQLServer JDBC Driver]Error establishing socket. Connection refused"" <br>The error occurred on line 96. error.message = Operation failed on the data source named ""FREE"". error.rootCause = coldfusion.runtime.ClientScopeDBException: Operation failed on the data source named ""FREE"".
Well, onError should catch everything, but it can't catch your syntax errors. A site wide error handler CAN catch that. So he will not _always_ be able to use onError.
First... thanks for a very informative post.
I'm trying to be good and implement a catch-all error handling process using application.cfc. I should also mention that I'm using Fusebox 5.5 and have called the "Super" scope accordingly where needed.
I've tried several variations using this post and other examples but I just can't seem to get detailed info emailed to me while also redirecting the user to a "pretty" page. I'm also writing errors to a log file but so far that doesn't seem to be a problem.
Currently the <cflocation> tag in the onError method of app.cfc is causing it's own forever looping exception. I can instead <cfoutput> the "pretty" message directly from app.cfc but that can leave half drawn pages and other ugly results visible. I also tried GetPageContext() using cfscript but it has the same results as <cflocation>.
I've read about setting up a check to filter "coldfusion.runtime.AbortException" but if I've read correctly that is no longer needed since 7.01 (I'm on 7.02)... either way it has no effect.
My other issue is that the most common errors I'm testing for seem to miss the onError method completely. I can trap them by placing a <cferror type="request" template="Error.cfm"> in the OnRequestStart method, but then I'm limited to how I can format the error page and I can't email the error to myself (as pointed out above). I may just have to accept that but it's worth asking if there might be a better alternative... especially since it seems this may catch more than onError. Would a Try/Catch in OnRequestStart give me more options... or just shoot me in the foot?
Any ideas, clarifications or updates would be very much appreciated.
You mention that if cflocation, you end up in a loop. Is the page you cflocate to also throwing an error? Like maybe you have a layout custom tag that throws an error. If you cflocate to an html page, I assume that works?
That's not EXACTLY it (no custom tags) but your suggestion helps a lot. FYI this is not a production site but more of a self-training exercise which I plan to use as a template for new CF sites down the road.
Anyway, I had the cflocation inside the onError method as in your example. To test, I was forcing an exception and seeing the exception error looping in my log file. I now find that the loop has nothing to do with exceptions. It's tied to cflocation.
I took out all of the onError stuff I was trying and then added some <cflog> tags to the methods in application.cfc. Then I just added a cflocation to one of my pages. It still loops but doesn't hit onError. I can see where new requests and sessions are being generated several times per second but never finishing. Fusebox is also reinitialized as part of the loop.
I have two identical files that I've tried to cflocate to. They are very basic and identical except one is named error.cfm and the other is error.htm. You are right that the htm file works just fine though I'm not sure what that tells me. Oh and I can also cflocate to an exernal url like "http://www.google.com" with no problem.
I suspect I'm just missing something in relation to launching fusebox 5.5 using application.cfc while also trying to do some other creative things with application.cfc. But hey, that's how we learn. If I never broke anything I wouldn't know how to fix anything.
I'm not a big fan of emailing verbose error dumps primarily due to security concerns. Also, verbose error info can take up a lot of room in log files and email storage, especially if you have a busy site that suddenly starts bugging out. Error logs are a pain to wade through as well.
I've developed a solution that stores errors as JSON data in a database. I only store the most recent 100 errors so that if a major glitch happens while I'm asleep it won't clog the database. When an error occurs, I send an email that only contains a basic message with the template and line number, and a link to my "error admin" which displays the verbose error data (that I stored as JSON) using cfdump.
Storing errors in a database lets me flag things that are fixed and things that are not using my "error admin", which is quite handy when I am procrastinating.
Oh and of course I convert the JSON data back to a CF object using deserializeJSON before displaying it with cfdump. Just wanted to make that clear.
I just put this code in my site. Thanks for this, it's great. I think finding a bug should have a reward as opposed to the "Sorry we have a problem" page, so I built this. It's entertaining and informative. The true story of the origin of the bug report. Hope you enjoy it.
http://infosavvygroup.com/b...
Ok, this is a year-old posting and the comment list is long so forgive me if someone else brought this up...
Anyone notice that when the request error runs (and possibly the other errors too) it puts the error message into the http response header? Is there any way to hide it there so all it shows is a generic 500 error instead of mentioning whatever is going wrong in my code?
I think you want to disable "Enable HTTP status codes" in the ColdFusion administrator.
Ah. But does that mean I'll have to add a cfheader to the error template to send the 500? Or am I misunderstanding how "Enable HTTP Status Codes" works?
Oh I'm sorry - I misread you. You don't want _anything_ about the error to be revealed. Is that what you meant?
Well, yeah, I guess so, I'm just following your suggestion to avoid putting error data even in the error_request.cfm file to keep snooping hackers from finding out what your code is up to. So I'm keeping myself just paranoid enough in case anyone wants to turn my site into a dedicated advertisement for enhancement drugs.
...Speaking of "never too much paranoia", didn't Adobe recently give an update to get ColdFusion to read .htm files like .cfm files? Talk about catering to the paranoid. :D
Hmm. I've got an error handler and it reveals nothing. Not even in the header. Maybe I'm not seeing what you are seeing.
Are you saying you have an error handler, it says something simple "Oops", and yet you can still sniff out the underlying error? (ie, db, bad variable, etc)
Hm. Well, I'm creating a unique situation so maybe it won't occur for exception type errors:
I never got around to defining a mail server on my development server, mostly due to procrastination. So when I created "test.cfm" the error.cfm threw an error because of the cfmail tag which then led to the error_request.cfm. When I then checked the HTTP header on the error_request.cfm I got this: "500 No SMTP server specified for the cfmail tag."
Perhaps it's because it's an error caused by my failure to setup the SMTP server or because it's the error_request template. Maybe?
Ah, your error handler threw an error. Try not to do that. ;) I replicated your setup, but my 500 error did NOT have 'no smtp', instead, it was just:
500 Internal Server Error
Nice and vague.
Oh right, I should have mentioned I was basing all this on the instructions you gave for using the cferror tags instead of the onError method.
Right, I reverted to App.cfm, and even then, I can't see the detailed error info in the response headers.
Hi,
I have spoiled my one day to practice the Coldfusion Validations and I couldnt pass this guy. validateAt is not working for 'onsubmit' or 'blur', working perfectly for 'onserver'.
here is my code
----------------
<html>
<head></head>
<body>
<cfform action="insert2.cfm" method="post">
Enter a Number:
<cfinput type="text" name="ValNumber" maxlength="3" required="yes" validate="integer" validateat="onsubmit">
<cfinput type="submit" name="submit" value="submit">
</cfform>
</body>
</html>
Any help is highly appreciated, thanks
Jems.
Do you have javascript enabled? Sounds like maybe not.
Just to show I'm not a total dunderhead in ColdFusion, let me show you what I've done with errors. This is coming from the onError() in application.cfc.
[Application.cfc]
<cffunction name="onError">
<cfargument name="Exception" required=true/>
<cfargument type="String" name="EventName" required=true/>
<cfif NOT (Arguments.EventName IS "onSessionEnd") OR (Arguments.EventName IS "onApplicationEnd")>
<cfinclude template="includes/dsp_allErrors.cfm">
</cfif>
</cffunction>
[dsp_allErrors.cfm]
<cfsavecontent variable="theError"> #arguments.exception.message#
#arguments.exception.detail#
#arguments.exception.tagContext[1].template# LINE #arguments.exception.tagContext[1].line# <cfif ISDEFINED("arguments.exception.sql")>
#arguments.exception.sql#
</cfif>
</cfsavecontent>
<cflog file="AppErrorLog" type="error" text="#theError#">
I just pull the info out that I need instead of doing a full dump. How many acutally use the stack dumps etc? I haven't used a stack dump since my machine language days (which is back when we had to slide beads around on a framework)
Of course the user only sees "YOU BROKE IT YOU MORON!" message and I get the email telling me to check the log.
Personally I would never display an error that
occured due to the server side process. But if a user
caused the error I would tell them. eg.) Invalid Email or something, but truthfully that really isn't an error it's just aninvalid input. I hope you guys understand what I'm talking about
Greetings. I seem to be having interesting behavior with my cferror handling. I was looking for some help. Currently, I only have a cferror catching errors of type "exception" The problem I am having is that not all "exception" type errors are being caught. My error page is setup to email me the occurrence of the error with all relevant details. However, intermittently the server will just throw the brief error message to the screen and not direct the user to the error page. The only way to get to the actual error template is to hit the refresh button on the browser and the user finally get directed to the template and the email gets sent out. I have no idea how many errors are actually occurring on the site. I have also installed the following patch which could be part of the problem:
http://www.adobe.com/suppor...
Please Advise.
Are you sure your error handling template isn't throwing an error itself?
I don't think it is because just as soon as I hit refresh, the page jumps to my template and the email gets sent out.
You got me on that one then. All I can recommend is maybe digging a bit more specifically into the cases where it happens and see if you can create a reproduceable case. When you can, share a zip.
I will try and put a zip together. Also, where i get confused is how the error trappings work. Let's say I have 2 error handlers one for type "exception" and one for type "request". Both in the same app.cfm. The cferror of type "request" seems to trap the error that would usually be trapped just by "exception" by itself. Is that the way it is supposed to work?
No - the request error should only fire if the exception error actually fails as well (ie your error handler having an error).
I read over the comments, and don't think I missed it, but I was wondering: why not use the site-wide handler?
I ask because we have a few hundred sites on two servers (CF v7 & 8). That means not only a mix of applicaiton cfc and cfm sites, but modifying all of the, and adding the error pages to each.
It seems like the site-wide option would be great in our case, but if there's a good/wise reason not to, I have no problem putting in the time to fix these individually if necessary.
Also, if we do it per-site, is it worth putting all of the error handing into a cfc for all the error pages to call? It seems like that at least would make ongoing management easier, but I wasn't sure if the nature of the error pages restricted that.
Why not use the site-wide handler? Well remember - this is a guide for _applications_, not the server as a whole. However, you bring up a good point. With hundreds of apps, using a site wide handler would cover those apps that do NOT do proper handling.
As to your second question - by 'all the error handling' - do you mean what to do with the exception info? Obviously you can't put onError in it's own CFC. It needs to be in App.cfc. I guess I'd say - if you are doing a -lot- with the errors, then maybe so. I typically just dump and mail (2 lines or so) so a CFC would be overkill.
You make it sound like cferror/onerror would supersede site-wide if they existed...is that the case? That site-wise is a fall-back when there is no error handing?
The second point was just trying to find a way manage all of the handling for the individual error pages from a single page. My idea would be to have all the error.cfm pages call a shared error.cfc, which in turn would do all of the handling.
Ultimately, I'd like to have the error info inserted into a database, instead of mailed (too many sites, multiple people administering, etc). This way, the error page could say "when contacting support, refer to error #1257", which would be the ID in the database where that error is held. It would also allow us to hold the dump in its entirety if we like.
Obviously, there may be adjustments that may need to be done along the way, so I just want to make adjustments to one cfm or cfc page that would have the code doing this, to avoid updating all of the error pages individually.
Yep, the app settings supersede the server wide settings.
So based on what you said - I think it _would_ make sense to abstract your error handling as you have described.
Just wanted to loop back and say thanks for the help. Our new error page works great, and storing the info in a DB for a more comprehensive error history has already proven to be a huge asset. It's also going to let us do some interesting forensics via queries over time to look for problems we would have missed in the past (consistent errors by IP, patterns to errors, etc).
One last question: Is there a way to alter the Error scope? I wanted to trim out the StackTrace sections or just clear the contents. We don't use them, and it would make reading the rest of the dump much easier. I have tried StructDelete and StructUpdate, but the former throws an error, and the latter just does nothing.
Interesting. So if you structDelete the stacktrace key it doesn't change it?
I've confirmed this. I'm going to write a blog entry on it. For now though I'd recommend copying the values to a new struct, and as you copy, you can trim.
I'll be interested to read that. If you could include how to create the copy to trim, I would be interested in that as well.
I tried StructCopy, but it is not supported for the Error structure (much like StructDelete). Duplicate works, but still cannot be altered.
I assume you mean by looping it, but I couldn't figure out how to do that when the structure of the Error var is dynamic depending on the error.
Here ya go:
http://www.coldfusionjedi.c...
Thank you Ray, fir this very useful post!
cheers!
Raymond,
I don't know a thing about CF but needed a "nice" error message for broken links to our old pages floating around Google. This magic formula works perfectly! I'm so grateful for the fully-functional templates you drew up, as I wouldn't be able to code any CF myself. Thanks!!
~Babs
Glad to help!
Hi Raymond!
Seems I spoke slightly too soon... the error message I receive in the mail is: Element MESSAGE is undefined in ERROR. For fun I deleted the "#error.message# -" part and left text="#error.diagnostics#" - at which point it started emailing me Element DIAGNOSTICS is undefined in ERROR. :D It does incidentally log the 404 errors though!
Odd - so what I'd suggest is simply dumping ERROR to an email and seeing what you get.
I make a cfhttp request to UPS for rates and sometimes this timesout or errors. This generates a request error. And using Application.cfm and cferror any CFML within my error page is ignored.
Does using Application.cfc and onError have the same limitation?
Well the obvious question is - have you tried? :) Make your timeout incredibly low, give it a whirl. As far as I know, It _should_ work, otherwise it wouldn't be terribly useful for error handling.
I currently have a similar issue - like the one Lance C reported above ;(http://www.coldfusionjedi.c...
I have a cferror tag - type=exception in the root application.cfm. The error template logs the info in a db log table. This works fine in most cases. Occasionally (a lot :() we see errors in the application log that should have been caught. In certain sub folders (which include root application.cfm) the users see the errors directly - the nice error template never shows up and does not get logged.
Any suggestions - please advice.
Thanks,
Ess Vee
Would the error perhaps be a DB issue? If so, then your error handler itself is throwing the error.
It was not a db error...It was a 'developer' issue!
I was trying to convert the error into a wddx object to store in the DB - and the cfwddx breaks on certain error structures like 'generatedContent' or 'stackTrace' or 'RootCause' - I deleted these structures - the essential info is already available - CFWDDX was happy and the CFERROR handler works.
Should not have doubted it in the first place...
Thank you for your guidance.
really helpful info...thanks
Hi Ray
Great article. We have setup our error handling in Application.cfm and it works exactly as you have pointed out.
In some places of the applicaiton however we use JS window.open for a popup to process a downloadable excel or other file. Do you know how to catch the errors that occur in a poup? It seems that if we switched everything to CFWindow it would work but would prefer to not have to do that as there are many points in the app that use the js window.open method. Currently if I throw a <cfjoe> (fake tag) into a cfm page called in a popup application is not catching it and the error is output in the popup - ugly!
Thanks for any advice,
Matt
Now that I have opened up this window.open can of worms I also realize another issue with using cferror int he application.cfm...
If you throw an error in a cfm page that is not in the root directory, but in a sub directory, then you end up with another cferror telling you the template (in my case errorEmail.cfm) can not be found.
I have searched extensively and others have this issue but I have nto found a solution to make it always use the errorEmail.cfm in the root. The only solution I can figure out is to add a page errorEmail.cfm in each subdirectory that simply does a cfinclude to the root errorEmail.cfm file and then in the main errorEmail.cfm where the page relocates at the end (we use js location="" to relocate in case the error is thrown in a modal and not in the main document.body) you just need to relocate to the correct path based on which subdirectory encountered the error.
I wondered if you had a different solution to this issue that might be more elegant.
Best,
Matt
If I remember right - you can make it a relative path from a cfmapping. So if / is mapped to your application root, then /error.cfm should work even if you are in a subdirectory.
Regarding my previous 2 posts...
1 - To handle the popups spawned by js window.open, in the template that is processed from the applicaiton.cfm error handle, rather than cflocation at end of page to relocate, this was changed to a js location() method. Before relocating it does a js try to see if (opener) exists. If it does, we are in a popup so we call opener.location() to move the main window to the clean error output page and then window.close() to close the popup.
2. I was wrong about things here..what is happening is that without any mapping if an error is thrown in a subdirectory it does call the proper template as specified in your applicaiton.cfm (or cfc), the one in the root directory of the application. However, when our page was trying to relocate it was not finding the page to relocate to which would throw another exception error and you get the template not found error. To solve, right before the location() method in the error handling template page, use coldfusion to determine the relative path of the subdirectory and set a variable (eg relPath="../../../") and then use that in the location () (eg location="#relPath#index.cfm?action=error...")
Hopefully these posts will help someone else.
Matt,
If I ask for a nonexistent page on my site, I get a request error. But I do have cferror set up in my application.cfm file, and the file called by that tag:
<cferror type="request" template="exception_req.cfm">
does exist and I can access it directly. But cferror doesn't call it. Wondering why...
You should use onMissingTemplate instead.
How do you something like this:
<cferror type="exception" template="index.cfm?layout=error.exception">
This doesn't work by the way.
Can you explain what you are trying to accomplish?
Sorry, we have a fusebox setup where everything is passing through index.cfm with fuses which redirect to specified templates. So when I set up the <cferror> path in application.cfm, I want to pass a fuseaction rather than a specific template. Hope that makes sense.
I don't know Fusebox, but I'd check their docs to see if they have native support to do some random fuseaction when an error occurs. I know Model-Glue has support for that.
If Fusebox doesn't, then I'd simply use cflocation to push the user from the error file to the fuseaction you want.
Hi Ray - this is brilliant. I wondered if this could be done in cfscript. I think this works but I'm not sure I have it complete.
public void function onError(required any exception, required string eventName){WriteDump(arguments);}
Cheers
Martin
You can do that in script - sure. You want an abort after the writedump though.
Thanks Ray.
I'm still getting used to cfscript - I like it a lot though.
Cheers
Martin
@Ray
Wanted to let you know I still find this useful. Great post.
Some tips for others who might be reading this:
I use an "application.isLive" boolean in addition to Ray's "admin" setting to determine whether I show the error dump, or sending and email and displaying a friendly apology.
I also have this little piece of code you might find useful:
<!--- capture two different forms of tag context --->
<cfset TagContext = StructFindKey(ARGUMENTS.exception, 'TagContext') />
<cfif ArrayLen(TagContext[1].value) EQ 0 AND IsDefined("ARGUMENTS.exception.RootCause.TagContext")>
<cfset TagContext = ArrayNew(1) />
<cfset TagContext[1] = StructNew() />
<cfset TagContext[1].value = ARGUMENTS.exception.RootCause.TagContext />
</cfif>
<!--- create a string of tag context and line numbers for the log --->
<cfloop from="1" to="#ArrayLen(TagContext)#" index="e">
<cfloop from="1" to="#ArrayLen(TagContext[e].value)#" index="c">
<cfset error_templates = ListAppend(error_templates, "#TagContext[e].value[c].template#: #TagContext[e].value[c].line#", " called from ") />
</cfloop>
</cfloop>
This basically creates a string using the cf error tag context to tell you the chain of files and lines numbers executed on the way to the error. It what I typically look at in the dump of an error because it's usually the fastest way to get to the place I need to be to fix it.
The tag context string is meant to be logged so you can get at it later, or you can watch it come across if you are tailing a log. I love this technique for monitoring a live server because you never can tell what a user, server, service, database, etc. is going to do that you didn't think of. This is a great way to see it the first time and every time it happens and have a consistent way to look at it.
For some reason CF sometimes issues as "RootCause" element in the error and sometimes it doesn't. I don't know why, but that's the reason for the two different methods of capturing the tag context.
The next thing I like to is this:
<cfset var error_id = GenerateSecretKey('DESEDE') />
or
<cfset var error_id = CreateUUID() />
.. whichever you prefer
Then I use this string in my logs, emails, messages to the customer, etc. That way they can all tie back to each other. Some people will say this is overkill. Maybe. But if your user comes to you with an error id and you can trace it back to the exact instance, time, etc of the error and see what actually happened, then you can be a hero. It's just good customer service I think.
TL;DR - Create tag context string for easy logging. Create unique error id for easier log searching.
Wanted to post really quick that I figured out an awesome way to thread similar emailed errors from your app. This is really great for reducing the number of unread emails [especially if you use gmail web interface].
All you have to do is take my tag context string example from above and encrypt it with a certain key so that it generates a consistent output if the tag context of the error is the same!
This is AWESOME [if I *do* say so myself].
Example:
<!--- create fallback subject --->
<cfset var error_subject = GenerateSecretKey('DESEDE') />
<!--- capture two different forms of tag context --->
<cfset TagContext = StructFindKey(ARGUMENTS.exception, 'TagContext') />
<cfif ArrayLen(TagContext[1].value) EQ 0 AND IsDefined("ARGUMENTS.exception.RootCause.TagContext")>
<cfset TagContext = ArrayNew(1) />
<cfset TagContext[1] = StructNew() />
<cfset TagContext[1].value = ARGUMENTS.exception.RootCause.TagContext />
</cfif>
<!--- create a string of tag context and line numbers for the log --->
<cfloop from="1" to="#ArrayLen(TagContext)#" index="e">
<cfloop from="1" to="#ArrayLen(TagContext[e].value)#" index="c">
<cfset error_templates = ListAppend(error_templates, "#TagContext[e].value[c].template#: #TagContext[e].value[c].line#") />
</cfloop>
</cfloop>
<!--- create an encrypted string from the tag context to allow email threading for similar errors --->
<cfif IsDefined("error_templates") AND error_templates NEQ "">
<!--- change the delimeter of the list to "called from " for more readability --->
<cfset error_templates = ListChangeDelims(error_templates, " called from ") />
<!--- generate unique email subject line ---->
<cfset error_subject = cfusion_encrypt(error_templates, 'MyApp version 0.1') />
</cfif>
Then just make your email subject something like subject="MyApp error: #error_subject#" and you too can have threading magic!
I pulled this code from Adobe. When I tried doing this, it basically creates an infinite redirect loop and I will get hundreds of emails.
<cfargument name="Exception" required="true" />
<cfargument name="EventName" type="string" required="true" />
<!--- Log all errors --->
<cflog file="#this.name#" type="error" text="Event Name: #EventName#">
<cflog file="#this.Name#" type="error" text="Message: #exception.message#">
<!--- Some exceptions, including SSI do not generate a rootcause structure --->
<cfif isDefined("exception.rootcause")>
<cflog file="#this.name#" type="error" text="Root Cause Message: #exception.rootcause.message#">
</cfif>
<cfif NOT (arguments.EventName IS onSessionEnd) OR (arguments.eventName IS onApplicationEnd)>
<cfinclude template="emailToDeveloper.cfm">
<cflocation url="errorPagec.cfm" addtoken="no">
</cfif>
In my App.cfc I check to see if a database is active. If not, the OnError does it's stuff.
Would it best to place the error page in a separate directory with another Application.cfc file that is perhaps blank?
I pretty much want the error message to display within the template I created.
Thanks in advance
It looks like your error template has an error itself. It could be anything. I'd comment out everything and then put stuff back in one line at a time.
if my application makes heavy use of <cfdiv> and ColdFusion.navigate I will use something like <cfinclude template="/Errors/error.cfm"> instead of <cflocation>
@Micheal white! Thank you very much for sharing this tip. I was searching for such stuff and now will use <cfinclude template="/Errors/error.cfm"> instead of <cflocation>. Thanks you resolved my problem.
Ray,
How do you handle Request errors when using onError in Application.cfc? I tried adding a CFERROR, but this does absolutely nothing. Is there somewhere in Application.cfc to handle Request errors?
Thanks.
Not quite sure what you mean. I do show an example of onError in the blog entry.
I'm referring to the "Oh Crap" errors. I.E. CFPOO in your onError handler....
So your asking me how to handle errors in onError itself?
I'm sorry I'm not being clear. In your example above, you give a great explanation of how to catch and respond to the "Oh Crap"/REQUEST exceptions that occur INSIDE your error handler when using CFERROR. I am not using CFERROR. I am using an onError handler in Application.cfc. So, my question is, how can I catch and respond to the "Oh Crap"/REQUEST exceptions that occur inside the onError handler in Application.cfc.
You would need to use try/catch, or use cferror _along_ with onError.
Dude,
As simple as this may look to some, this is the best error reporting routine we've ever used. Makes our development and bug fixing so much easier and more productive.
Thank you so much for sharing this.
Gregory
Happy to help!
Raymond
Do you know if it's possible to reference the "Exceptions" data shown in Coldfusion's debugging output? It was my assumption initially that this would correspond to TagContext, but I've found that's not the case, at list in my system. It uses a modified fusebox 3 architecture, antiquated I know, but can't do much about that. There's an index.cfm which includes the core fusebox template which handles everything else. A cfcatch around that include handles errors for the site. If I dump cfcatch, the tagContext just shows the fusebox file and the index, but not the "deep" template where the error actually occurred. In the standard debugging, however, the correct exception location is shown, for example...
Exceptions
17:25:55.055 - Expression Exception - in /somepath/dsp_myDeepTemplate.cfm : line 2
Element GARBAGE is undefined in REQUEST.
17:25:55.055 - Application Exception - in /somepath/fbx_fusebox.cfm : line 247
Element GARBAGE is undefined in REQUEST.
What you are seeing in the debug template is a high level look at _all_ exceptions. It is _not_ the same as the data you get in _one_ caught exception. You should, for one exception, be able to get tag context. Are you not seeing that?
On Sept 11, 2009 you wrote "Yep, the app settings supersede the server wide settings." but that doesn't seem to be the case. My hosting provider has a value in the Site-Wide Error Hander area but no matter what I do, my cfapplication/cferror does not fire. It just shows their global Oops page. The only way around it seems to be cftry blocks inline with a cfdump in the catch area. Any reason this would happen? The execution order of operations SEEMS like application would supercede server so I can't figure out how the host template keeps cutting in line.
In my testing, I don't see that. Ensure your cferror template doesn't have an error itself. Reduce it down to "Oops" and see if that works.
Spectacularly late to the party on this one, but how is one supposed to handle the case where a user navigates directly to application.cfm, application.cfc, or onrequestend.cfm? I've bounced around to various sites on the web (including this one!), and I see an un-trapped CF error which says this:
Invalid request of Application.cfm, Application.cfc, or OnRequestEnd.cfm file
That's super ugly! Gah!
I'm surprised here for two reasons: 1) how is it that I've never actually tried this before, and 2) how is it that CF doesn't have some simple fix for this?
It appears as if there is no way to handle this. I'd do it at the web server level. Just push any request for it to /index.html or some such.
Wow, that's...awful. )-: But thanks for the reply.
@Ben the way to handle (conceal) that is by using a global error handler, set in CF Admin.
Really good info! Our CF app sorely needs some of this. I often find it easier to read about stuff like this from people who have used it rather than from the coldfusion reference. Thanks for sharing it!!
Glad this old post is still useful. :)
Very useful post. Thanks!
With regard to the log file...when you do <cflog file="myapperrorlog">, where is this stored? I tried creating my own, but can't find it. Also, in the CF Administrator, you're only allowed the log files: Application.log, server.log, exception.log, EventGateway.log, and Derby.log. I'm on CF9. I also tried creating a log file in my C:\Coldfusion9\logs\ dir, but no dice.
What am I doing wrong?
Thanks!
It is supposed to be in the logs directory with the rest of them. If you are not seeing them in logs, nor in the CF admin, then something funky is going on. Like your CF service not having write permissions for the folder. I'd check to see if you have CF running as a user.
Ray,
Thanks so much for this post.
I did notice that if you have in your query string <r=Y you get >r=Y so i made a quick change to your Application.cfc
replace cgi.query_string with #HTMLEditFormat(cgi.query_string)#
Thanks again,
Ernie
Thanks that helped me a lot. A great tutorial to build a nice error page.
I have an issue where <cferror> is getting triggered, but there are no exception variables (error.rootcause, error.message, error.type, etc). Why would this happen?
No clue. Perhaps an error in your event handler itself? Maybe try cfdump Variables and see if you see anything weird?
I've dumped the Variables scope, and it's empty. The error handler isn't the problem, I think, because the exception variable is trigged when other errors occur. Something on the website is triggering <cferror> but causes <cferror> to not populate the exception variables.
You got me there. I'd have to dig onto your server to know why.
I was having a similar issue. Seems that in IIS7 you can set the error page globally by clicking on your server name in the left pane, then going to Error Pages, and specifying a local CFM of your choosing. What this does is bypass CF and run the error processing page, but without the error scope (since it was IIS who launched the page, and not CF,and really, what would IIS know?).
So, all I can think of is creating a separate error-iis500.cfm page that does everything but include the error scope in the email. At least I'll know something happened, and on what page, but not the line number or the specific error.
BTW, I found this...
http://forums.adobe.com/thr...
If you make the change using appcmd as recommended and decide you don't like what it does, you can set it back with -existingResponse:Replace
Hi,
i need an issue for testing if CF is running.
We have a server running on windows 2008 R2 and sometimes CF server give no response. (Not a 404 message).
I plan to write a script on another server which will test the firts ...
Any idea ?
No ideas outside of checking the logs.
Hey Ray,
Would it be possible for you to display this in script? We are moving to all script in our CFCs and reference material for script is woefully short. Thanks!
What in particular?
I was referring to the error handling solution you written in tag format above. I did come up with this:
public void function onError(any Exception, string EventName) output="true" {
include 'sorry.cfm';
var errorText = "";
var errorEmail = new mail();
savecontent variable="errorText" {
writeDump("#application.GetApplicationSettings()#");
writeOutput("Form:");
writeDump("#form#");
writeOutput("URL:");
writeDump("#url#");
writeoutput("CGI:");
writeDump("#CGI#");
}
errorEmail.setTo('name@ename.com');
errorEmail.setFrom('mailServer');
errorEmail.setSubject('An Error has Occured');
errorEmail.setBody('
http://#cgi.server_namecgi.script_name#?#cgi.query_string#<br />
Time: #dateFormat(now(), "short")##timeFormat(now(), "short")#<br />
EventName: #eventName#<br />
Message: #arguments.exception.message#<br />
Details: #arguments.exception.detail#<br />
Type: #arguments.exception.type#<br />
Error Dump: #errorText#
');
errorEmail.setType('html');
errorEmail.send();
}
Any suggestions welcome.
Old topic but I am stuck. I am having a pathing issue only when error.cfm is not used as error handler. It is a ajax call to a cfc. exception log:
"ERROR","ajp-bio-8009-exec-7","02/23/2016","08:49:25","",";Page /secure/file_transfer/SSStatus.cfc [zip://C:\inetpub\wwwroot\archive-Carlson (15).ra!/secure/file_transfer/SSStatus.cfc] not found;Page /secure/file_transfer/SSStatus.cfc [zip://C:\inetpub\wwwroot\archive-Carlson (15).ra!/secure/file_transfer/SSStatus.cfc] not found"
the cfc is definitely in that path.