Exceptions include any event that disrupts the normal flow of instructions in a ColdFusion page, such as failed database operations, missing include files, or developer-specified events. Ordinarily, when ColdFusion encounters an exception, it stops processing and displays an error message, or an error page specified by a cferror tag or the Site-wide Error Handler option on the Settings page in the Administrator. However, you can use the ColdFusion exception handling tags to catch and process runtime exceptions directly in ColdFusion pages.
This ability to handle exceptions directly in your application pages enables your application to do the following:
- Respond appropriately to specific errors within the context of the current application page
- Recover from errors whenever possible.
Exception-handling tags
ColdFusion provides the exception-handling tags listed in the following table:
Tag |
Description |
---|---|
If any exceptions occur while processing the tag body, look for a cfcatch tag that handles the exception, and execute the code in the cfcatch tag body. |
|
Execute code in the body of this tag if the exception caused by the code in the cftry tag body matches the exception type specified in this tag's attributes.Used in cftry tag bodies only. |
|
Generate a user-specified exception. |
|
Exit the current cfcatch block and generates a new exception of the same type. Used only in cfcatch tag bodies. |
Using cftry and cfcatch tags
The cftry tag lets you go beyond reporting error data to the user:
- You can include code that recovers from errors so your application can continue processing without alerting the user.
- You can create customized error messages that apply to the specific code that causes the error.
For example, you can use cftry to catch errors in code that enters data from a user registration form to a database. The cfcatch code could do the following:
- Retry the query, so the operation succeeds if the resource was only temporarily unavailable.
- If the retries fail:
- Display a custom message to the user
- Post the data to an e-mail address so the company staff can enter the data after the problem has been solved.
Code that accesses external resources such as databases, files, or LDAP servers where resource availability is not guaranteed is a good candidate for using try/catch blocks.
Try/catch code structure
In order for your code to directly handle an exception, the tags in question must appear within a cftry block. It is a good idea to enclose an entire application page in a cftry block. You then follow the cftry block with cfcatch blocks, which respond to potential errors. When an exception occurs within the cftry block, processing is thrown to the cfcatch block for that type of exception.
Here is an outline for using cftry and cfcatch to handle errors:
<cftry> |
Try/catch code rules and recommendations
Follow these rules and recommendations when you use cftry and cfcatch tags:
- The cfcatch tags must follow all other code in a cftry tag body.
You can nest cftryblocks. For example, the following structure is valid:
<cftry>
code that may cause an exception
<cfcatch ...>
<cftry>
First level of exception handling code
<cfcatch ...>
Second level of exception handling code
</cfcatch
</cftry>
</cfcatch>
</cftry>If an exception occurs in the first level of exception-handling code, the inner cfcatch block can catch and handle it. (An exception in a cfcatch block cannot be handled by cfcatch blocks at the same level as that block.)
- ColdFusion always responds to the latest exception that gets raised. For example, if code in a cftry block causes an exception that gets handled by a cfcatch block, and the cfcatch block causes an exception that has no handler, ColdFusion displays the default error message for the exception in the cfcatch block, and you are not notified of the original exception.
- If an exception occurs when the current tag is nested inside other tags, the CFML processor checks the entire stack of open tags until it finds a suitable cftry/cfcatch combination or reaches the end of the stack.
- Use cftry with cfcatch to handle exceptions based on their point of origin within an application page, or based on diagnostic information.
- The entire cftry tag, including all its cfcatch tags, must be on a single ColdFusion page. You cannot place the <cftry> start tag on one page and have the </cftry> end tag on another page.
- For cases when a cfcatch block is not able to successfully handle an error, consider using the cfrethrow tag, as described in Using the cfrethrow tag in Handling runtime exceptions with ColdFusion tags.
If an exception can be safely ignored, use a cfcatchtag with no body; for example:
<cfcatch Type = Database />
- In problematic cases, enclose an exception-prone tag in a specialized combination of cftry and cfcatch tags to immediately isolate the tag's exceptions.
Exception information in cfcatch blocks
Within the body of a cfcatch tag, the active exception's properties are available in a cfcatch object. The object contents are described as follows:
Standard cfcatch variables
The following table describes the variables that are available in most cfcatch blocks:
Property variable |
Description |
---|---|
cfcatch.Detail |
A detailed message from the CFML compiler. This message, which can contain HTML formatting, can help to determine which tag threw the exception. The cfcatch.Detail value is available in the CFScript catch statement as the exceptionVariable parameter. |
cfcatch.ErrorCode |
The cfthrow tag can supply a value for this code through the errorCode attribute. For Type="Database", cfcatch.ErrorCode has the same value as cfcatch.SQLState. Otherwise, the value of cfcatch.ErrorCode is the empty string. |
cfcatch.ExtendedInfo |
Custom error message information. This is returned only to cfcatch tags for which the type attribute is Application or a custom type.Otherwise, the value of cfcatch.ExtendedInfo is the empty string. |
cfcatch.Message |
The exception's default diagnostic message, if one was provided. If no diagnostic message is available, this is an empty string.The cfcatch.Message value is included in the value of the CFScript catch statement exceptionVariable parameter. |
cfcatch.RootCause |
The Java servlet exception reported by the JVM as the cause of the "root cause" of the exception. |
cfcatch.TagContext |
An array of structures structure containing information for each tag in the tag stack The tag stack consists of each tag that is currently open. |
cfcatch.Type |
The exception's type, returned as a string. |
Note: If you use the cfdump tag to display the cfcatch variable, the display does not include variables that do not have values. |
The cfcatch.TagContext variable contains an array of tag information structures. Each structure represents one level of the active tag context at the time when ColdFusion detected the exception. That is, there is one structure for each tag that is open at the time of the exception. For example, if the exception occurs in a tag on a custom tag page, the tag context displays information about the called custom tag and the tag in which the error occurs.
The structure at position 1 in the array represents the currently executing tag at the time the exception was detected. The structure at position arrayLen represents the initial tag in the stack of tags that were executing when the compiler detected the exception.
The following table lists the tagContext structure attributes:
Entry |
Description |
---|---|
Column |
Obsolete (retained for backwards compatibility). Always 0. |
ID |
The tag in which the exception occurred. Exceptions in CFScript are indicated by two question marks (??). All custom tags, including those called directly, are identified as cfmodule. |
Line |
The line on the page in which the tag is located. |
Raw_Trace |
The raw Java stack trace for the error. |
Template |
The pathname of the application page that contains the tag. |
Type |
The type of page; it is always a ColdFusion page. |
Database exceptions
The following additional variables are available whenever the exception type is database:
Property variable |
Description |
---|---|
cfcatch.NativeErrorCode |
The native error code associated with this exception. Database drivers typically provide error codes to assist in the diagnosis of failing database operations. The values assumed by cfcatch.NativeErrorCode are driver-dependent.If no error code is provided, the value of cfcatch.nativeErrorCode is -1. The value is 0 for queries of queries. |
cfcatch.SQLState |
The SQLState code associated with this exception. Database drivers typically provide error codes to assist in the diagnosis of failing database operations. SQLState codes are more consistent across database systems than native error codes. If the driver does not provide an SQLState value, the value of cfcatch.SQLState is -1. |
cfcatch.Sql |
The SQL statement sent to the data source. |
cfcatch.queryError |
The error message as reported by the database driver. |
cfcatch.where |
If the query uses the cfqueryparam tag, query parameter name-value pairs. |
Expression exceptions
The following variable is only available for Expression exceptions:
Property variable |
Description |
---|---|
cfcatch.ErrNumber |
An internal expression error number, valid only when type="Expression". |
Locking exceptions
The following additional information is available for exceptions related to errors that occur in cflock tags:
Property variable |
Description |
---|---|
cfcatch.lockName |
The name of the affected lock. This is set to "anonymous" if the lock name is unknown. |
cfcatch.lockOperation |
The operation that failed. This is set to "unknown" if the failed operation is unknown. |
Missing include exceptions
The following additional variable is available if a missing file specified by a cfinclude tag causes the error.
Property variable |
Description |
---|---|
cfcatch.missingFileName |
The name of the missing file. |
Using the cftry tag: an example
The following example shows the cftry and cfcatch tags. It uses the cfdocexamples data source, which many of the examples listed here use, and a sample included file, includeme.cfm.
If an exception occurs when you run the cfquery statement, the application page flow switches to the cfcatch type="Database" exception handler. It then resumes with the next statement after the cftry block, once the cfcatch type="Database" handler completes. Similarly, the cfcatch type="MissingInclude" block handles exceptions raised by the cfinclude tag.
<!--- Wrap code you want to check in a cftry block ---> |
Use the following procedure to test the code.
Test the code
- Make sure that there is no includeme.cfm file and display the page. The cfcatch type="MissingInclude" block displays the error.
- Create a nonempty includeme.cfm file and display the page. If your database is configured properly, you see an employee entry and do not get any error.
In the cfquerytag, change the line:
FROM Employee
to:
FROM Employer
Display the page. This time the cfcatch type="Database" block displays an error message.
Change Employer to Employee. Change the cfoutputline:
<p>Department: #Dept_ID#<br>
to:
<p>Department: #DepartmentID#<br>
Display the page. This time the cfcatch type="Any" block displays an error message indicating an expression error.
Change DepartmentID back to Dept_ID and redisplay the page. The page displays properly.Open \CFusion\Log\MyAppPage.log in your text editor. You should see a header line, an initialization line, and four detail lines, like the following:
"Severity","ThreadID","Date","Time","Application","Message"
"Information","web-0","11/20/01", "16:27:08",, "cf_root\runtime\servers\default\logs\ MyAppPage.log initialized"
"Information","web-0","11/20/01","16:27:08",,
"Page: web_root/MYStuff/MyDocs/ cftryexample.cfm Error: MissingInclude"
"Information","web-1","11/20/01","16:27:32",,"
Page: web_root/MYStuff/MyDocs/ cftryexample.cfm Error: "
"Information","web-0","11/20/01","16:27:49",,
"Page: web_root/MYStuff/MyDocs/ cftryexample.cfm Error: Database"
"Information","web-1","11/20/01","16:28:21",,
"Page: web_root/MYStuff/MyDocs/ cftryexample.cfm Error: General Exception"
"Information","web-0","11/20/01","16:28:49",,
"Page: web_root/MYStuff/MyDocs/ cftryexample.cfm Error: "
Reviewing the code
The following table describes the code:
Code |
Description |
|
---|---|---|
|
Initializes the employee ID to a valid value. An application would get the value from a form or other source.Sets the default errorCaught variable value to the empty string (to indicate no error was caught).There is no need to put these lines in a cftry block. |
|
|
Starts the cftry block. Exceptions from here to the end of the block can be caught by cfcatch tags.Queries the cfdocexamples database to get the data for the employee identified by the EmpID variable. |
|
|
Begins the HTML page. This section contains all the code that displays information if no errors occur.Includes the includeme.cfm page.Displays the user information record from the test query. |
|
|
Handles exceptions thrown when a page specified by the cfinclude tag cannot be found. Displays cfcatch variables, including the ColdFusion basic error message, detail message, and the name of the file that could not be found.Sets the errorCaught variable to indicate the error type. |
|
|
Handles exceptions thrown when accessing a database. Displays cfcatch variables, including the ColdFusion basic error message, the error code and SQL state reported by the databases system, and the detailed error message.Sets the errorCaught variable to indicate the error type. |
|
|
Handles any other exceptions generated in the cftry block.Since the error can occur after information has displayed (in this case, the contents of the include file), draws a line before writing the message text.Displays the ColdFusion basic and detailed error message.Sets the errorCaught variable to indicate the error type. |
|
|
Ends the HTML page, then the cftry block. |
Using the cfthrow tag
You can use the cfthrow tag to raise your own, custom exceptions. When you use the cfthrow tag, you specify any or all of the following information:
Attribute |
Meaning |
---|---|
type |
The type of error. It can be a custom type that has meaning only to your application, such as InvalidProductCode. You can also specify Application, the default type. You cannot use any of the predefined ColdFusion error types, such as Database or MissingTemplate. |
message |
A brief text message indicating the error. |
detail |
A more detailed text message describing the error. |
errorCode |
An error code that is meaningful to the application. This field is useful if the application uses numeric error codes. |
extendedInfo |
Any additional information of use to the application. |
All of these values are optional. You access the attribute values in cfcatch blocks and Exception type error pages by prefixing the attribute with either cfcatch or error, as in cfcatch.extendedInfo. The default ColdFusion error handler displays the message and detail values in the Message pane and the remaining values in the Error Diagnostic Information pane.
Catching and displaying thrown errors
The cfcatch tag catches a custom exception when you use any of the following values for the cfcatch type attribute:
- The custom exception type specified in the cfthrow tag.
- A custom exception type that hierarchically matches the initial portion of the type specified in the cfthrow tag. For more information, see the next section, Custom error type name hierarchy.
- Application, which matches an exception that is thrown with the Application type attribute or with no type attribute.
- Any, which matches any exception that is not caught by a more specific cfcatch tag.
Similarly, if you specify any of these types in a cferror tag, the specified error page displays information about the thrown error.
Because the cfthrow tag generates an exception, a Request error handler or the Site-wide error handler can also display these errors.
Custom error type name hierarchy
You can name custom exception types using a method that is similar to Java class naming conventions: domain name in reverse order, followed by project identifiers, as in the following example:
<cfthrow |
This fully qualified naming method is not required; you can use shorter naming rules, for example, myApp.Invalid_field.codeValue, or even codeValue.
This naming method is not just a convention; ColdFusion uses the naming hierarchy to select from a possible hierarchy of error handlers. For example, assume that you use the following cfthrow statement:
<cfthrow type="MyApp.BusinessRuleException.InvalidAccount"> |
Any of the following cfcatch error handlers would handle this error:
<cfcatch type="MyApp.BusinessRuleException.InvalidAccount"> |
The handler that most exactly matches handles the error. In this case, the MyApp.BusinessRuleException.InvalidAccount handler runs. However, if you used the following cfthrow tag:
<cfthrow type="MyApp.BusinessRuleException.InvalidVendorCode |
the MyApp.BusinessRuleException handler receives the error.
The type comparison is not case sensitive.
When to use the cfthrow tag
Use the cfthrow tag when your application can identify and handle application-specific errors. One typical use for the cfthrow tag is in implementing custom data validation. The cfthrow tag is also useful for throwing errors from a custom tag page to the calling page.
For example, on a form action page or custom tag used to set a password, the application can determine whether the password entered is a minimum length, or contains both letters and number, and throw an error with a message that indicates the password rule that was broken. The cfcatch block handles the error and tells the user how to correct the problem.
Using the cfrethrow tag
The cfrethrow tag lets you create a hierarchy of error handlers. It tells ColdFusion to exit the current cfcatch block and "rethrow" the exception to the next level of error handler. Thus, if an error handler designed for a specific type of error cannot handle the error, it can rethrow the error to a more general-purpose error handler. The cfrethrow tag can only be used in a cfcatch tag body.
The cfrethrow tag syntax
The following pseudocode shows how you can use the cfrethrow tag to create an error-handling hierarchy:
<cftry> |
Although this example uses a Database error as an example, you can use any cfcatch type attribute in the innermost error type.
Follow these rules when you use the cfrethrow tag:
- Nest cftry tags, with one tag for each level of error handling hierarchy. Each level contains the cfcatch tags for that level of error granularity.
- Place the most general error catching code in the outermost cftry block.
- Place the most specific error catching code in the innermost cftry block.
- Place the code that can cause an exception error at the top of the innermost cftry block.
- End each cfcatch block except those in the outermost cftry block with a cfrethrow tag.
Example: using nested tags, cfthrow, and cfrethrow
The following example shows many of the discussed techniques including nested cftry blocks and the cfthrow and cfrethrow tags. The example includes a simple calling page and a custom tag page:
- The calling page does little more than call the custom tag with a single attribute, a name to be looked up in a database. It does show, however, how a calling page can handle an exception thrown by the custom tag.
- The custom tag finds all records in the cfdocexamples database with a matching last name, and returns the results in a Caller variable. If it fails to connect with the main database, it tries a backup database.
The calling page
The calling page represents a section from a larger application page. To keep things simple, the example hard-codes the name to be looked up.
<cftry> |
Reviewing the code
The following table describes the code:
Code |
Description |
|
---|---|---|
|
In a cftry block, calls the cf_getEmps custom tag (getEmps.cfm). |
|
|
If the tag throws an exception indicating that it did not receive a valid attribute, catches the exception and displays a message, including the message variable set by the cfthrow tag in the custom tag. |
|
|
If the tag returns a result, uses the cfdump tag to display it. (A production application would not use the cfdump tag.) |
The custom tag page
The custom tag page searches for the name in the database and returns any matching records in a getEmpsResult variable in the calling page. It includes several nested cftry blocks to handle error conditions. For a full description, see Reviewing the code section, following the example:Save the following code as getEmps.cfm in the same directory as the calling page.
<!--- If the tag didn't pass an attribute, throw an error to be handled by |
Reviewing the code
The following table describes the code:
Code |
Description |
|
---|---|---|
|
Makes sure the calling page specified an EmpName attribute. If not, throws a custom error that indicates the problem and exits the tag. The calling page handles the thrown error. |
|
|
If the tag has an EmpName attribute, does the remaining work inside an outermost try block. The cfcatch block at its end handles any otherwise-uncaught exceptions. |
|
|
Starts a second nested try block. This block catches exceptions in the database query. If there are no exceptions, sets the calling page's getEmpsResult variable with the query results. |
|
|
If the query threw a Database error, checks to see if the error was caused by an inability to access the database (indicated by an SQLState variable value of S100 or IM002). If the database was not found, starts a third nested try block and tries accessing the backup database. This try block catches exceptions in this second database access.If the database inquiry succeeds, sets the calling page's getEmpsResult variable with the query results. |
|
|
If the second database query failed with a database error, gives up silently. Because the Database type cfcatch tag does not have a body, the tag exits. The calling page does not get a getEmpsResult variable. It cannot tell whether the database had no match or an unrecoverable database error occurred, but it does know that no match was found. |
|
|
If the second database query failed for any other reason, throws the error up to the next try block.Ends the innermost try block |
|
|
In the second try block, handles the case in which the first database query failed for a reason other than a failure to find the database. Rethrows the error up to the next level, the outermost try block. |
|
|
In the second try block, catches any errors other exceptions and rethrows them up to the outermost try block. Ends the second try block. |
|
|
In the outermost try block, handles any exceptions by displaying an error message that includes the exception type and the exception's error message. Because there was no code to try that is not also in a nested try block, this cfcatch tag handles only errors that are rethrown from the nested blocks.Exits the custom tag and returns to the calling page.Ends the catch block, try block, and initial cfif block. |
Testing the code
To test the various ways errors can occur and be handled in this example, try the following:
- In the calling page, change the attribute name to any other value; for example, My Attrib. Then change it back.
- In the first cfquery tag, change the data source name to an invalid data source; for example, NoDatabase.
- With an invalid first data source name, change the data source in the second cfquery tag to cfdocexamples.
- Insert cfthrow tags throwing custom exculpations in various places in the code and observe the effects.