Bug In Struct.Each() Error Handling In Adobe ColdFusion
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:
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:
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
I've filed a bug, CF-4222576 - Struct.each() surfaces incorrect error when callback throws an error.
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →