-
Notifications
You must be signed in to change notification settings - Fork 11k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[5.6] Handle more JSON errors gracefully when JSON_PARTIAL_OUTPUT_ON_ERROR is set #23410
[5.6] Handle more JSON errors gracefully when JSON_PARTIAL_OUTPUT_ON_ERROR is set #23410
Conversation
…is set It's fairly common for stack traces to be circular, i.e. they can't be encoded as JSON without limiting the recursion depth somehow. PHP automatically does this if JSON_PARTIAL_OUTPUT_ON_ERROR is true, but we still considered it an error. By treating JSON_ERROR_RECURSION as okay if JSON_PARTIAL_OUTPUT_ON_ERROR is set we prevent things from blowing up if the user uses a JSON-based exception handler. Since exceptions thrown in exception handlers are generally a recipe for disaster I added JSON_ERROR_INF_OR_NAN to the list of "acceptable" errors too.
1930a19
to
01090f7
Compare
Need some example of what happens before this PR and how to recreate it so we can see what you are talking about. Please resubmit with that. |
@taylorotwell we use a custom exception handler in a Lumen application that renders exceptions as JSON. Without this patch, when a particular exception happens, what you get is this: [
{
"file": "/vagrant/api/vendor/illuminate/http/JsonResponse.php",
"line": 75,
"function": null,
"class": "Symfony\\Component\\Debug\\Exception\\FatalErrorException",
"args": [
"Uncaught InvalidArgumentException: Recursion detected in /vagrant/api/vendor/illuminate/http/JsonResponse.php:75\nStack trace:\n#0 /vagrant/api/vendor/symfony/http-foundation/JsonResponse.php(50): Illuminate\\Http\\JsonResponse->setData(Array)\n#1 /vagrant/api/vendor/illuminate/http/JsonResponse.php(31): Symfony\\Component\\HttpFoundation\\JsonResponse->__construct(Array, 500, Array)\n#2 /vagrant/api/app/App/Exceptions/Handler.php(51): Illuminate\\Http\\JsonResponse->__construct(Array, 500, Array, 512)\n#3 /vagrant/api/vendor/nordsoftware/lumen-chained-exception-handler/src/ChainedExceptionHandler.php(57): ALehdet\\ContentApi\\App\\Exceptions\\Handler->render(Object(Illuminate\\Http\\Request), Object(InvalidArgumentException))\n#4 /vagrant/api/vendor/laravel/lumen-framework/src/Concerns/RegistersExceptionHandlers.php(127): Nord\\Lumen\\ChainedExceptionHandler\\ChainedExceptionHandler->render(Object(Illuminate\\Http\\Request), Object(InvalidArgumentException))\n#5 /vagrant/api/vendor/laravel/lumen-framework/src/Concerns/RegistersExcep"
]
},
{
"file": "/vagrant/api/vendor/laravel/lumen-framework/src/Concerns/RegistersExceptionHandlers.php",
"line": 54,
"function": "handleShutdown",
"class": "Laravel\\Lumen\\Application",
"args": []
},
{
"file": "[internal]",
"line": 0,
"function": "Laravel\\Lumen\\Concerns\\{closure}",
"class": "Laravel\\Lumen\\Application",
"args": []
}
] With this patch, the real exception that caused this (and subsequently couldn't be encoded due to recursion) is shown: {
"type": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
"message": "Type error: Argument 1 passed to Contentful\\Delivery\\ResourceBuilder::buildField() must be an instance of Contentful\\Delivery\\ContentTypeField, null given, called in /vagrant/api/vendor/contentful/contentful/src/Delivery/ResourceBuilder.php on line 372",
"file": "/vagrant/api/vendor/contentful/contentful/src/Delivery/ResourceBuilder.php",
"line": 385,
"trace": [
{
"file": "/vagrant/api/vendor/contentful/contentful/src/Delivery/ResourceBuilder.php",
"line": 385,
"function": null,
"class": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
"args": [
"Type error: Argument 1 passed to Contentful\\Delivery\\ResourceBuilder::buildField() must be an instance of Contentful\\Delivery\\ContentTypeField, null given, called in /vagrant/api/vendor/contentful/contentful/src/Delivery/ResourceBuilder.php on line 372"
]
},
... SNIP
]
} Since the purpose of the the |
@taylorotwell for refernce, our error handler looks like this (it uses whoops to format the exception): public function render($request, Exception $e)
{
if (env('APP_DEBUG', false)) {
$data = Formatter::formatExceptionAsDataArray(new Inspector($e), env('APP_DEBUG', false));
} else {
$data = $this->createDefaultResponseData($e);
}
$status = $this->resolveResponseStatusCode($e);
return new JsonResponse($data, $status, [], JSON_PARTIAL_OUTPUT_ON_ERROR);
} The last line is the important one where we say we want partial output whenever possible. |
It's fairly common for stack traces to be circular, i.e. they can't be encoded as JSON without limiting the recursion depth somehow. PHP automatically does this if JSON_PARTIAL_OUTPUT_ON_ERROR is true, but we still considered it an error.
By treating JSON_ERROR_RECURSION as okay if JSON_PARTIAL_OUTPUT_ON_ERROR is set we prevent things from blowing up if the user uses a JSON-based exception handler.
Since exceptions thrown in exception handlers are generally a recipe for disaster I added JSON_ERROR_INF_OR_NAN to the list of "acceptable" errors too.