Skip to content

Commit 1ca579d

Browse files
authored
fix(OpenAI): don't bail out early on text/plain - only if JSON parsing errors occurred. (#736)
* test(OpenAI): add assertion for invalid_request_error * fix(OpenAI): no early bail out on content-type text/plain
1 parent eab26e6 commit 1ca579d

File tree

2 files changed

+36
-7
lines changed

2 files changed

+36
-7
lines changed

src/Transporters/HttpTransporter.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ public function requestStringOrObject(Payload $payload): AdaptableResponse
8686

8787
$contents = (string) $response->getBody();
8888

89+
$this->throwIfRateLimit($response);
90+
$this->throwIfJsonError($response, $contents);
91+
8992
if (str_contains($response->getHeaderLine('Content-Type'), ContentType::TEXT_PLAIN->value)) {
9093
return AdaptableResponse::from($contents, $response->getHeaders());
9194
}
9295

93-
$this->throwIfRateLimit($response);
94-
$this->throwIfJsonError($response, $contents);
95-
9696
try {
9797
/** @var array{error?: array{message: string, type: string, code: string}} $data */
9898
$data = json_decode($contents, true, flags: JSON_THROW_ON_ERROR);
@@ -163,10 +163,6 @@ private function throwIfJsonError(ResponseInterface $response, string|ResponseIn
163163
return;
164164
}
165165

166-
if (! str_contains($response->getHeaderLine('Content-Type'), ContentType::JSON->value)) {
167-
return;
168-
}
169-
170166
if ($contents instanceof ResponseInterface) {
171167
$contents = (string) $contents->getBody();
172168
}
@@ -179,6 +175,11 @@ private function throwIfJsonError(ResponseInterface $response, string|ResponseIn
179175
throw new ErrorException($data['error'], $response);
180176
}
181177
} catch (JsonException $jsonException) {
178+
// Due to some JSON coming back from OpenAI as text/plain, we need to avoid an early return from purely content-type checks.
179+
if (! str_contains($response->getHeaderLine('Content-Type'), ContentType::JSON->value)) {
180+
return;
181+
}
182+
182183
throw new UnserializableResponse($jsonException, $response);
183184
}
184185
}

tests/Transporters/HttpTransporter.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,34 @@
112112
});
113113
})->with('request methods');
114114

115+
test('request object mismatched project error', function (string $requestMethod) {
116+
$payload = Payload::list('models');
117+
118+
$response = new Response(401, ['Content-Type' => 'text/plain; charset=utf-8'], json_encode([
119+
'error' => [
120+
'message' => 'OpenAI-Project header should match project for API key',
121+
'type' => 'invalid_request_error',
122+
'code' => 'mismatched_project',
123+
'param' => null,
124+
],
125+
'status' => 401,
126+
]));
127+
128+
$this->client
129+
->shouldReceive('sendRequest')
130+
->once()
131+
->andReturn($response);
132+
133+
expect(fn () => $this->http->$requestMethod($payload))
134+
->toThrow(function (ErrorException $e) {
135+
expect($e->getMessage())->toBe('OpenAI-Project header should match project for API key')
136+
->and($e->getErrorMessage())->toBe('OpenAI-Project header should match project for API key')
137+
->and($e->getErrorCode())->toBe('mismatched_project')
138+
->and($e->getErrorType())->toBe('invalid_request_error')
139+
->and($e->getStatusCode())->toBe(401);
140+
});
141+
})->with('request methods');
142+
115143
test('request object server errors', function () {
116144
$payload = Payload::create('completions', ['model' => 'gpt-4']);
117145

0 commit comments

Comments
 (0)