Skip to content

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

@tsilia

Description

@tsilia

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