Is the `error` object ever _not_ serializable?

This is a strange question. But, many years ago, @Daniel_Short had this post outlining that the error object in a cfcatch is not always a struct. Because we were also running into this issue in Adobe ColdFusion 8 (maybe 9?), our root error logging would pass the error object through duplicate() in order to “struct-ify it” before we serialized it. Over the years, this has been translated into Lucee CFML, and looks like this now (heavily truncated):

// Log error to output stream (K8 consumes it).

systemOutput( serializeJson( duplicate( error ) ), true, true );

So, my crazy question is: in “modern ColdFusion”, is there ever any reason why I can’t just call serializeJson() on the error object directly without duplicating it first? Is that just an old issue that is no longer relevant? One that was perhaps never relevant in Lucee (only ACF)?

I suspect that Adobe ColdFusion’s cfcatch is not actually a struct. I think that, in the vast majority of situations, ColdFusion automatically casts it to a struct under the hood.

In fact, you can see from the following test that cfcatch and duplicate(cfcatch) are different classes.

The test consists of just 1 CFM file

<!--- testpage.cfm --->
	<cfcatch type="any" >
		<cfset errorClass={}>
		<cfset errorClass.as_is=getMetadata(cfcatch).getName()>
		<cfset errorClass.duplicate=getMetadata(duplicate(cfcatch)).getName()>
		<cfdump var="#errorClass#" label="Error class">


@BK_BK is correct. In ACF, catch blocks weren’t really structs per se, they just acted like them and CFDump rendered this like structs. I’m not really sure how ACF 2021 handles them since ACF is closed source, but we can prove that catch objects ARE proper structs for sure in Lucee. A catch object is an instance of lucee.runtime.exp.CatchBlockImpl which extends StructImpl here:

which extends StructSupport here

which extends the Struct interface here


Ok awesome, so at least in the Lucee CFML world, I should always be OK to serialize an error object (assuming I’m not dealing with some funky 3rd party Java lib that creates a strange implementation of an error).

It doesn’t matter if the exception was thrown from a java library, so long as it was caught with a CFML catch block, it will be wrapped in the classes above. You can always get the native exception object out, but the outer wrapper will always be a CatchBlockImpl.

Note, if you called a Java library that simply returned a Throwable as the output of a method, that would NOT be the same as a CF catch object.

Ah, nice! I don’t really know the constraints of the underlying Throwable stuff - my fear was that some library would create an Error where – as a silly example – the .detail property was actually a “Closure” and not a “String” … and then trying to serialize it might go :boom: But, sounds like that’s just Fear Uncertainty Doubt (FUD) on my part.

To be clear, it’s not a “constraint” so much as an apple and orange :slight_smile: While a CFML catch object often times contains a Java Throwable inside of it that it wraps, the rule of thumb is so long as it came from CF, it should have what you expect. For example, Java exceptions don’t even have a “detail”. That’s uniquely a CF thing.

:muscle: Thank you for the clarification.