Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Profiler (?) adds HTML to the JSON error output and client is unable to parse response as JSON #59024

Copy link
Copy link
Open
@tsilia

Description

@tsilia
Issue body actions

Symfony version(s) affected

7.0, 7.1

Description

Hello.
I'm struggling to get a valid JSON API response in case of PHP fatal error when using built-in Symfony server.
I have an API controller (it has attribute format: "json") which can fail with an exception, and I wrote JsonProblemNormalizer (as adviced here: Overriding Error output for non-HTML formats)

//...
class JsonProblemNormalizer implements NormalizerInterface
{
    public function normalize($exception, ?string $format = null, array $context = []): array
    {
        return [
            'status' => 'error',
            'message' => $exception->getMessage(),
            'code' => $exception->getStatusCode(),
        ];
    }
    // ...
}

It lets my API controller return exception message in a JSON object which I then use on client side to display an error message.
But I'm getting weird response, like this:

<br />
<b>Fatal error</b>:  Maximum execution time of 30 seconds exceeded in <b>REDACTED/vendor/symfony/serializer/Serializer.php</b> on line <b>269</b><br />
{"status":"error","message":"Error: Maximum execution time of 30 seconds exceeded","code":500}

The error itself is legit, but the problem is that the response seems to be a JSON prepended by some HTML which obviously can't be parsed on the client side as a valid JSON.

UPD.
I use symfony serve as a development web-server. Looks like this command adds an HTML snippet to every response on a PHP fatal error.

How to reproduce

  1. Start built-in Symfony server:
    symfony serve
  2. Define some controller file, like BrokenJsonResponsePocController.php
  3. Write some API controller method:
use Symfony\Component\HttpFoundation\JsonResponse;
// ...
#[Route('/api_broken_json_response_poc', name: 'api_broken_json_response_poc', methods: ['GET'], format: "json")]
public function api_broken_json_response_poc(Request $request): JsonResponse
{
    set_time_limit(5);
    // hopefully this will exceed 5 seconds of maximum allowed time for script execution:
    $idx = 0;
    while ($idx < 9223372036854775807)
        $idx++;
    return $this->json(['idx' => $idx]);
}
  1. On client side, make a request:
fetch('/api_broken_json_response_poc')
    .then(response => response.json())
    .then(json => {
        console.log(JSON.stringify(json))
    })
    .catch(err => {
        console.log(err)
    })
  1. Look at the request in browser dev console and observe the following on the 'Response' tab:
<br />
<b>Fatal error</b>:  Maximum execution time of 5 seconds exceeded in <b>.../src/Controller/BrokenJsonResponsePocController.php</b> on line <b>64</b><br />
{"status":"error","message":"Error: Maximum execution time of 5 seconds exceeded","code":500}

Possible Solution

No response

Additional Context

I'm using PHP 8.2.26 on Linux Mint 20.3 Una (the base is Ubuntu 20.04).

Also, in dev environment, if you do this:

use Symfony\Component\HttpFoundation\JsonResponse;
// ...
#[Route('/api_broken_json_response_poc', name: 'api_broken_json_response_poc', methods: ['GET'], format: "json")]
public function api_broken_json_response_poc(Request $request): JsonResponse
{
    throw new RuntimeException('Unhandled runtime exception');
}

then on client side you get normal JSON in the response without any HTML:

{"status":"error","message":"Unhandled runtime exception","code":500}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.