Summary
When a CakePHP exception chains a previous exception via the third constructor argument, MixerApiExceptionRenderer collects the full exception chain but passes raw Throwable objects into the JSON serialization layer. Since PHP's Exception class has only protected properties (message, code, file, line, trace, previous) and does not implement JsonSerializable, json_encode produces {} for each one.
This means chained exception details are completely lost in the JSON error response.
Before (current behavior)
{
"exception": "FailedRouteCacheException",
"message": "Unable to cache route collection...",
"exceptions": [{}, {}]
}
The inner exception's message -- which often contains the actual root cause -- is invisible to the developer.
After (expected behavior)
{
"exception": "FailedRouteCacheException",
"message": "Unable to cache route collection...",
"exceptions": [
{
"class": "FailedRouteCacheException",
"message": "Unable to cache route collection...",
"code": 500
},
{
"class": "RuntimeException",
"message": "Serialization of 'Closure' is not allowed",
"code": 0
}
]
}
Location
plugins/exception-render/src/MixerApiExceptionRenderer.php, lines 76-81:
$exceptions = [$exception];
$previous = $exception->getPrevious();
while ($previous != null) {
$exceptions[] = $previous;
$previous = $previous->getPrevious();
}
This array of raw Throwable objects is passed to $this->controller->set() and serialized via JsonView.
Discovered via
The CakeDC\CachedRouting\Routing\Middleware\CachedRoutingMiddleware throws a FailedRouteCacheException that wraps the original exception. The wrapper's message says "the original exception message can show what type of object failed to serialize" -- but the renderer never surfaces that original message, making the error misleading.
Summary
When a CakePHP exception chains a previous exception via the third constructor argument,
MixerApiExceptionRenderercollects the full exception chain but passes rawThrowableobjects into the JSON serialization layer. Since PHP'sExceptionclass has onlyprotectedproperties (message,code,file,line,trace,previous) and does not implementJsonSerializable,json_encodeproduces{}for each one.This means chained exception details are completely lost in the JSON error response.
Before (current behavior)
{ "exception": "FailedRouteCacheException", "message": "Unable to cache route collection...", "exceptions": [{}, {}] }The inner exception's message -- which often contains the actual root cause -- is invisible to the developer.
After (expected behavior)
{ "exception": "FailedRouteCacheException", "message": "Unable to cache route collection...", "exceptions": [ { "class": "FailedRouteCacheException", "message": "Unable to cache route collection...", "code": 500 }, { "class": "RuntimeException", "message": "Serialization of 'Closure' is not allowed", "code": 0 } ] }Location
plugins/exception-render/src/MixerApiExceptionRenderer.php, lines 76-81:This array of raw Throwable objects is passed to
$this->controller->set()and serialized viaJsonView.Discovered via
The
CakeDC\CachedRouting\Routing\Middleware\CachedRoutingMiddlewarethrows aFailedRouteCacheExceptionthat wraps the original exception. The wrapper's message says "the original exception message can show what type of object failed to serialize" -- but the renderer never surfaces that original message, making the error misleading.