{"action":"created","issue":{"url":"https://api.github.com/repos/facebook/hhvm/issues/4818","labels_url":"https://api.github.com/repos/facebook/hhvm/issues/4818/labels{/name}","comments_url":"https://api.github.com/repos/facebook/hhvm/issues/4818/comments","events_url":"https://api.github.com/repos/facebook/hhvm/issues/4818/events","html_url":"https://github.com/facebook/hhvm/issues/4818","id":57256201,"number":4818,"title":"Empty response body on fatal errors","user":{"login":"Yogu","id":395810,"avatar_url":"https://avatars.githubusercontent.com/u/395810?v=3","gravatar_id":"","url":"https://api.github.com/users/Yogu","html_url":"https://github.com/Yogu","followers_url":"https://api.github.com/users/Yogu/followers","following_url":"https://api.github.com/users/Yogu/following{/other_user}","gists_url":"https://api.github.com/users/Yogu/gists{/gist_id}","starred_url":"https://api.github.com/users/Yogu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yogu/subscriptions","organizations_url":"https://api.github.com/users/Yogu/orgs","repos_url":"https://api.github.com/users/Yogu/repos","events_url":"https://api.github.com/users/Yogu/events{/privacy}","received_events_url":"https://api.github.com/users/Yogu/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":4,"created_at":"2015-02-10T23:44:42Z","updated_at":"2015-02-17T20:10:29Z","closed_at":null,"body":"For reasonable error pages, we register a shutdown callback that will output a nice page when it detects that the script has not terminated gracefully. This works fine with the normal php engine, but in hhvm, it only works for non-fatal errors. For fatal errors, like calling an undefined function, the response body is not sent.\r\n\r\nThe following script:\r\n\r\n```\r\n<?php\r\necho \"before\\n\";\r\nregister_shutdown_function(function() {\r\n        header('Test: Header');\r\n        echo \"shutdown\\n\";\r\n});\r\ncall_undefined();\r\n```\r\n\r\nsends the header, but suppresses the whole response body:\r\n\r\n```\r\n< HTTP/1.1 500 hphp_invoke\r\n< Server: nginx/1.6.2\r\n< Date: Tue, 10 Feb 2015 23:42:16 GMT\r\n< Content-Type: text/html; charset=utf-8\r\n< Transfer-Encoding: chunked\r\n< Connection: keep-alive\r\n< X-Powered-By: HHVM/3.5.0\r\n< Test: Header\r\n< \r\n```\r\n\r\nI'm using the hhvm package on ubuntu 14.04 via fastcgi with nginx and the defaults for `server.ini` and `php.ini`.\r\n\r\nSee also [this stackoverflow question](https://stackoverflow.com/questions/23086903/hhvm-fastcgi-nginx-how-to-make-it-display-fatal-errors-in-the-browser) which has 12 upvotes but no satisfying answer."},"comment":{"url":"https://api.github.com/repos/facebook/hhvm/issues/comments/74742874","html_url":"https://github.com/facebook/hhvm/issues/4818#issuecomment-74742874","issue_url":"https://api.github.com/repos/facebook/hhvm/issues/4818","id":74742874,"user":{"login":"Yogu","id":395810,"avatar_url":"https://avatars.githubusercontent.com/u/395810?v=3","gravatar_id":"","url":"https://api.github.com/users/Yogu","html_url":"https://github.com/Yogu","followers_url":"https://api.github.com/users/Yogu/followers","following_url":"https://api.github.com/users/Yogu/following{/other_user}","gists_url":"https://api.github.com/users/Yogu/gists{/gist_id}","starred_url":"https://api.github.com/users/Yogu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yogu/subscriptions","organizations_url":"https://api.github.com/users/Yogu/orgs","repos_url":"https://api.github.com/users/Yogu/repos","events_url":"https://api.github.com/users/Yogu/events{/privacy}","received_events_url":"https://api.github.com/users/Yogu/received_events","type":"User","site_admin":false},"created_at":"2015-02-17T20:10:29Z","updated_at":"2015-02-17T20:10:29Z","body":"@paulbiss @iansltx Thanks for the tip with `implicit_flush`, that did the trick.\r\n\r\n@iansltx Thanks for `call_user_handler_on_fatals`. I need to use it because `error_get_last()` does not report the source of fatal errors if called in the shutdown function, we need to capture those details in the error handler for fatal errors.\r\n\r\nIn case anyone is interested, this is the gist of our error handling code:\r\n\r\n```\r\nclass ExceptionHandling {\r\n\tprivate static $uncaught_exception = null;\r\n\tprivate static $fatal_error = null;\r\n\r\n\tpublic static function shutdown_callback() {\r\n\t\tif (defined('GRACEFUL_SHUTDOWN')) {\r\n\t\t\t// everything went fine\r\n\t\t\tob_end_flush();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\theader('Status: 500 Internal Server Error');\r\n\t\tinclude(APP_PATH.'/errors/500.php');\r\n\t}\r\n\r\n\t// turn errors into exceptions\r\n\tpublic static function exception_error_handler($errno, $errstr, $errfile, $errline) {\r\n\t\tif (error_reporting() === 0) {\r\n\t\t\treturn false; // codefatal used @ to suppress errors\r\n\t\t}\r\n\r\n\t\t$exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);\r\n\r\n\t\t// hhvm can report fatals as errors. We can not convert them to exceptions, however, because they will\r\n\t\t// not trigger the exception handler and the stack trace is wrong, anyway. We need to save the errfile and\r\n\t\t// errline here because they are not available in the shutdown function.\r\n\t\t// for the errno, see https://github.com/facebook/hhvm/blob/master/hphp/runtime/base/runtime-error.h#L57\r\n\t\tif ($errno & (1 << 24)) {\r\n\t\t\tself::$fatal_error = array(\r\n\t\t\t\t\t'message' => $errstr,\r\n\t\t\t\t\t'type' => $errno,\r\n\t\t\t\t\t'file' => $errfile,\r\n\t\t\t\t\t'line' => $errline\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tthrow $exception;\r\n\t}\r\n\r\n\tpublic static function exception_handler($exception) {\r\n\t\tvar_dump('exHandler');\r\n\t\tself::$uncaught_exception = $exception;\r\n\t}\r\n\r\n\t/**\r\n\t * @return Exception|null the exception that caused the app to crash, or null if there was none\r\n\t */\r\n\tpublic static function get_uncaught_exception() {\r\n\t\treturn self::$uncaught_exception;\r\n\t}\r\n\r\n\t/**\r\n\t * Gets the last error that has occurred, be it fatal or non-fatal\r\n\t * @return array with the fields message, type, file and line\r\n\t */\r\n\tpublic static function get_last_error() {\r\n\t\tif (self::$fatal_error) {\r\n\t\t\treturn self::$fatal_error;\r\n\t\t}\r\n\t\treturn error_get_last();\r\n\t}\r\n}\r\nregister_shutdown_function(array('ExceptionHandling', 'shutdown_callback'));\r\nset_error_handler(array('ExceptionHandling', \"exception_error_handler\"));\r\nset_exception_handler(array('ExceptionHandling', 'exception_handler'));\r\n```\r\n\r\n`500.php` then either uses `ExceptionHandling::get_uncaught_exception()` or `ExceptionHandling::get_last_error()`, whichever is present."}}