DevLearningTools

MODULE 12 · LESSON 01

<cftry>

Catching exceptions with <cftry>: its real structural rules (no attributes, at least one cfcatch required), guaranteed cleanup with cffinally, and nesting a try block around just one risky step in a larger process.

New lessons are added one at a time as the course gets built out — a graded quiz for each lesson is still on the way.

Exceptions are events that disrupt a page's normal flow, a failed database query, a missing include file, dividing by zero. cftry lets code catch one of these when it happens and decide what to do next, instead of letting ColdFusion's own default error page take over. cftry itself is deliberately simple: it takes no attributes at all, its only job is grouping risky code together with one or more cfcatch blocks (covered in depth in the next lesson) that decide what happens if something goes wrong.

Learning Objectives

After completing this lesson, you'll be able to:

  • Write a basic cftry/cfcatch block, and know cftry's real structural rules.
  • Nest a try block around one specific risky step, without changing how the rest of a larger process is handled.
  • Use cffinally for cleanup that must run no matter which step failed, or whether anything failed at all.

How cftry Fits In

Code That Might Fail

Exception Thrown?

cfcatch Handles It

cffinally Always Runs

A Basic cftry / cfcatch Block

Tag Syntax
<cftry>
    <cfset result = 10 / arguments.divisor>
    <cfcatch type="any">
        <cfoutput>Something went wrong: #cfcatch.message#</cfoutput>
    </cfcatch>
</cftry>
NOTE

cftry cannot stand alone with zero cfcatch blocks, at least one is mandatory. The next lesson covers cfcatch's own attributes and the exception object it exposes in real depth.

The Anatomy of cftry

cftry

no attributes

≥ 1 cfcatch

required

cffinally

optional

NOTE

cffinally, if present, always runs last, regardless of what happened above it, whether the try succeeded, a cfcatch handled a failure, or the cfcatch itself hit a problem.

A Real Example: Nesting try Around Just One Risky Step

A checkout process has several steps, checking inventory, charging a payment method, creating the order record. If only the payment step can reasonably be retried, wrapping just that step in its own nested cftry lets it be handled differently from a failure anywhere else in the process.

Outer try: Whole Checkout

Inner try: Payment Step Only

Inner catch Handles It, or Propagates Out

Tag Syntax
<cftry>
    <cfset checkInventory(orderItems)>

    <cftry>
        <cfset chargePaymentMethod(customer, orderTotal)>
        <cfcatch type="application">
            <cfset flash.message = "Your payment could not be processed, please try again.">
            <cflocation url="/checkout/retry-payment" addtoken="false">
        </cfcatch>
    </cftry>

    <cfset createOrderRecord(customer, orderItems)>

    <cfcatch type="any">
        <cfset flash.message = "Something went wrong completing your order.">
        <cflocation url="/checkout/error" addtoken="false">
    </cfcatch>
</cftry>
NOTE

A payment failure gets its own specific, retry-friendly message. A failure anywhere else (inventory, order creation, or a payment exception of a type the inner catch doesn't match) falls through to the outer catch's more general handling.

A Real Example: Guaranteed Cleanup With cffinally

Tag Syntax
<cftry>
    <cflock name="inventory-update-#productId#" type="exclusive" timeout="10">
        <cfset updateStockLevel(productId, quantityChange)>
    </cflock>
    <cfcatch type="any">
        <cflog text="Stock update failed for product #productId#: #cfcatch.message#">
    </cfcatch>
    <cffinally>
        <cfset removeFile(expandPath("./locks/#productId#.lock"))>
    </cffinally>
</cftry>
NOTE

The lock file is removed whether the stock update succeeded, failed, or the catch block itself encountered a problem, cffinally is the one place that's guaranteed to run.

Common Beginner Mistakes

Writing a cftry block with no cfcatch at all

This isn't valid, cftry requires at least one cfcatch block. There's no way to have a try with only a finally and no catch.

Nesting a try block around an entire large process instead of just the risky step

Wrapping everything in one broad try means every step gets the exact same generic handling. Nesting a smaller try around just the step that genuinely needs different treatment keeps the rest of the handling specific and clear.

Assuming an inner catch's failure to match still stops the exception

If a nested try's catch blocks don't match the exception's type, it propagates outward to the next enclosing try's catch, it isn't silently swallowed just because it passed through an inner try first.

Best Practices

  • Nest a try specifically around the one step that needs distinct handling, rather than wrapping an entire multi-step process in a single generic catch.
  • Use cffinally for anything that must happen regardless of outcome, releasing a lock, closing a connection, deleting a temporary file.
  • Keep the code inside cftry focused on the operations that can actually fail, not unrelated logic that happens to sit nearby.

Interview Questions

Does cftry itself take any attributes?

No, cftry has no attributes of its own. All of the configuration, the type being matched and so on, lives on its cfcatch (and optional cffinally) blocks.

Is a cftry block valid with zero cfcatch blocks?

No, at least one cfcatch is required. cffinally alone, without any cfcatch, isn't a valid cftry.

Why nest a try block around just one step of a larger process, instead of wrapping the whole thing?

It lets that one step be handled differently, a retry-friendly message for a payment failure, for example, while every other kind of failure in the process still falls through to a more general outer handler.

What happens if a nested try's catch blocks don't match the exception that was thrown?

The exception propagates outward to the next enclosing try's catch block, it isn't discarded just because it passed through an inner try that didn't handle it.

Summary

In this lesson, you wrote a basic cftry/cfcatch block, covered cftry's real structural rules (no attributes, at least one cfcatch required), nested a try block around a single risky step so it could be handled differently from the rest of a larger process, and used cffinally for cleanup guaranteed to run regardless of outcome.

What's Next?

The next lesson covers cfcatch specifically: its own attributes, the full set of built-in exception types, and the different fields each type actually adds to the caught exception.