client icon indicating copy to clipboard operation
client copied to clipboard

[Bug]: UnserializableResponse when Files::retrieve() returns HTML 503

Open bkremenovic opened this issue 4 months ago • 2 comments

Description

When calling Files::retrieve($fileId) via the PHP SDK, intermittent 503 responses from the API edge (HTML “503 Service Temporarily Unavailable” from OpenAI's nginx) cause the transporter to attempt json_decode() on the HTML body. This triggers:

OpenAI\Exceptions\UnserializableResponse
Syntax error
vendor/openai-php/client/src/Transporters/HttpTransporter.php:62

Because the response isn’t JSON, the SDK error is misleading and hides the actual HTTP status/body. This makes handling/retrying 5xx conditions difficult.

Steps To Reproduce

  1. Environment (example):

    • PHP: 8.x
    • SDK: openai-php/client (latest at time of issue)
  2. Code (minimal):

    $fileId = 'file_XXXXXXXXXXXX';
    
    // Repro: call retrieve on an existing file id
    $file = $client->files()->retrieve($fileId);
    
  3. Intermittently, the API responds with a 503 HTML page (edge/nginx). If you dump the raw contents in the transporter you’ll see something like:

    <html>
    <head><title>503 Service Temporarily Unavailable</title></head>
    <body>
    <center><h1>503 Service Temporarily Unavailable</h1></center>
    <hr><center>nginx</center>
    </body>
    </html>
    
  4. The SDK attempts to json_decode() this HTML and throws:

    OpenAI\Exceptions\UnserializableResponse (Syntax error)
    at vendor/openai-php/client/src/Transporters/HttpTransporter.php:62
    

OpenAI PHP Client Version

v0.16.0

PHP Version

8.4.6

Notes

  • The error originates here:

    • vendor/openai-php/client/src/Transporters/HttpTransporter.php:62
    • The transporter always tries to decode the body as JSON before considering that it might be an HTML error page.

bkremenovic avatar Sep 04 '25 21:09 bkremenovic

Thanks for report. I was under impression this was fully fixed. I'll take your sample and dig into what went wrong.

iBotPeaches avatar Sep 05 '25 10:09 iBotPeaches

O wait - an UnserializableResponse is what I expect. Its not JSON - that's the expectation. It used to crash much uglier with a type issue, this time its an exception.

The gap is we have is endpoints that don't return JSON (audio, file contents, etc) - those have it much more difficult. Since we are under assumption that JSON is what is being returned we go under that expectation.

So I believe next step here is one of following:

  • Make it more clear the UnserializableResponse is the one to use, extend with statusCode so you don't have to parse the Response object on it to identify HTTP code.
  • Introduce a new top-level error class (or shove this into ErrorException). Maybe anything 5xx automatically falls into it. Since we have handling for some 4xx already.
  • Do nothing - this has answered your query.

iBotPeaches avatar Sep 05 '25 10:09 iBotPeaches