Skip to main content
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Aaron Garland
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Aaron Garland

Bug In Struct.Each() Error Handling In Adobe ColdFusion

By
Published in Comments (1)

This is a bug I just ran into this morning while using the Struct.each() member method. If you throw a custom error inside an .each() iteration, Adobe ColdFusion—in at least 2021 and 2023—won't surface the error properly. Instead, it will surface a "wrapper" error that obfuscates the original type and message.

This only appears to affect the member method access. Meaning, the bug occurs when consuming struct.each(), but not when consuming structEach().

To see the bug, let's compare the .map() output, which works properly, with .each() output, which contains the bug. Within each iterator / operator / callback, I'm going to throw an error and then dump-out the resultant error structure:

<cfscript>

	values = {
		"hello": "world",
		"foo": "bar"
	};

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// This version works as expected.
	try {

		writeOutput( "<h2>Struct.Map Error</h2>" );

		values.map(
			() => {

				throw(
					type = "MyError",
					message = "My custom error message in struct.map()."
				);

			}
		);

	} catch ( any error ) {

		writeDump(
			var = error,
			show = "type,message,rootCause"
		);

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// This version HAS A BUG.
	try {

		writeOutput( "<h2>Struct.Each Error</h2>" );

		values.each(
			() => {

				throw(
					type = "MyError",
					message = "My custom error message in struct.each()."
				);

			}
		);

	} catch ( any error ) {

		writeDump(
			var = error,
			show = "type,message,rootCause"
		);

	}

</cfscript>

As you can see, all we're doing here is using throw() to raise an exception inside the .map() and .each() calls. And, when we run this in Adobe ColdFusion 2023, we get the following output:

Two ColdFusion error structures shown next to each other, the first for Struct.Map and the second for Struct.Each.

As you can see in the first error output, for struct.map(), the top-level type and message properties are representative of the custom error we threw. However, in the second output, for struct.each(), the same error is twice nested, making it harder to consume the error in a meaningful way.

While this has a direct impact on any local typed error handling you might try to apply via the catch block, you can leverage the Elvis operator to unwrap nested errors in your general error handling workflow.

In the follow code, we're using the exact same control-flow; but, instead of outputting the error directly, we're passing it through a custom unwrapError() function:

<cfscript>

	values = {
		"hello": "world",
		"foo": "bar"
	};

	/**
	* I attempt to unwrap and surface the deepest cause of the given error.
	*/
	public any function unwrapError( required any error ) {

		return (
			error.rootCause.rootCause.rootCause ?:
			error.rootCause.rootCause ?:
			error.rootCause ?:
			error
		);

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	try {

		writeOutput( "<h2>Struct.Map Error (Unwrapped)</h2>" );

		values.map(
			( key, value ) => {

				throw(
					type = "MyError",
					message = "My custom error message in struct.map()."
				);

			}
		);

	} catch ( any error ) {

		writeDump(
			var = unwrapError( error ),
			show = "type,message,rootCause"
		);

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	try {

		writeOutput( "<h2>Struct.Each Error (Unwrapped)</h2>" );

		values.each(
			( key, value ) => {

				throw(
					type = "MyError",
					message = "My custom error message in struct.each()."
				);

			}
		);

	} catch ( any error ) {

		writeDump(
			var = unwrapError( error ),
			show = "type,message,rootCause"
		);

	}

</cfscript>

This time, when we run the Adobe ColdFusion 2023 code, we get the following output:

Two ColdFusion error structures shown next to each other, both with similar output structures.

As you can see, this time, both error outputs contain the expected top-level error properties relating to our raised exceptions.

I'll open an issue in the ColdFusion bug tracker and posted it in the comments.

Want to use code from this post? Check out the license.

Reader Comments

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel