Intermittent Bug In serializeJSON() In Adobe ColdFusion 2025
In one of the recent Adobe ColdFusion 2025 updates (maybe 7, maybe 8), I seem to be hitting a strange intermittent bug in the serializeJson() function. It only happens a handful of times a day; and in my recent debugging efforts, I've found that running a small sleep() and then re-trying the call seems to work. This is why I think it's a bug in ColdFusion itself and not in my code.
At the top of every request to my site, I generate a Content Security Policy (CSP) payload. Part of this payload includes a JSON-stringification call:
<cfscript>
var reportPayload = serializeJson({
group: "csp-endpoint",
max_age: 10886400,
endpoints: [
{
"url": reportToUrl
}
]
});
</cfscript>
There's nothing request-specific in this payload. The reportToUrl is a configuration value that never changes. And my site gets hits thousands of times a day with no problem. Except, on 5-6 requests, this serializeJson() call throws this nonsensical error:
Invalid argument value for
serializeJSON.The
SerializeQueryargument can be a boolean or string type only.
After a bunch of failed debugging steps — assuming it was my fault — I finally tried adding a sleep(100) and a retry. In the following code, notice that the serializeJson() call in each try block is the identical:
<cfscript>
var reportPayload = {
group: "csp-endpoint",
max_age: 10886400,
endpoints: [
{
"url": reportToUrl
}
]
};
try {
// ..... THIS CALL IS IDENTICAL TO NEXT ONE .....
var reportValue = serializeJson( reportPayload );
} catch ( any error ) {
logger.error( "Couldn't JSON CSP data (A).", { reportPayload } );
sleep( 100 );
try {
// ..... THIS CALL IS IDENTICAL TO PREV ONE .....
var reportValue = serializeJson( reportPayload );
} catch ( any error2 ) {
logger.error( "Couldn't JSON CSP data (B).", { reportPayload } );
rethrow;
}
}
</cfscript>
If the error were in my code, I would expect both the logger.error() calls to show up in Bugsnag. However, when I look at my logging after running this all day, here's what I get:
As you can see, only the (A) version of the logging is recorded. After the sleep(100), the repeated call to serializeJson() works without error; and the (B) version never shows up.
What I assume happened is that there must have been some sort of "security fix" introduced to the serializeJson() function which has inadvertently introduced a transient bug of its own. I will open a ticket and link it in the comments.
UPDATE: 2025-05-30
After publishing this yesterday, I went to create a "hotfix" method for serializeJson(). And, while writing that method, I wanted to see if maybe the sleep(100) wasn't actually necessary. So my hotfix method includes two fallback retries, one with a sleep, one without:
component {
/**
* HOTFIX: I provide a version of the serializeJson() method that runs mulitple
* attempts on failure. This is to patch an emergent bug in one of the latest ACF
* updates that seems to have introduced some timing wonkiness.
*/
public string function serializeJsonHotfix( required any input ) {
try {
return serializeJson( input );
} catch ( any error ) {
logger.info( "Serialize JSON hotfix (A)" );
}
try {
return serializeJson( input );
} catch ( any error2 ) {
logger.info( "Serialize JSON hotfix (B)" );
}
sleep( 100 );
try {
return serializeJson( input );
} catch ( any error3 ) {
logger.info( "Serialize JSON hotfix (C)" );
rethrow;
}
}
}
As you can see, (B) is just an immediate retry of the same serializeJson() call - no sleep at all. Then (C) is a second retry with a sleep(100). I deployed this version this morning and so far, all I've seen in the error logs are:
Serialize JSON hotfix (A)
Turns out that the immediate retry is sufficient to work around the bug. At least in my particular case.
Want to use code from this post? Check out the license.
Reader Comments
I've filed issue CF-4232120 in the Adobe Tracker.
I just posted an update that an immediate retry seems to be sufficient. But, the truth is, my log-line (posted above in the "Update") does its own
serializeJson()call internally to prepare the payload for Bugsnag. So it's possible that this internally call either:Provided enough delay.
Rejiggered the internal Java state enough.
Not sure. Either way, I'm now 1000% convinced this is a true ACF bug.
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →