WordPress / WordPress/php-ai-client

Guzzle request options cause HTTP error responses to bypass ResponseUtil

Open
#265 0 comments 0 reactions 1 assignee View on GitHub

@superdav42 is already working on this.

Since Jul 28, 2026.

[Type] Bug Needs Dev origin:interactive status:in-review
Dominant language
PHP
Stars
308
Forks
84
Avg merge
7d 21h
Merged PRs (30d)
2

Description

Summary

When HttpTransporter has non-null RequestOptions and discovers a Guzzle-shaped client, it calls Guzzle's send() method with translated options. The options currently omit http_errors => false, so Guzzle can throw for 4xx/5xx responses before the response reaches ResponseUtil::throwIfNotSuccessful().

This leaves callers with the generic wrapper message HTTP client error occurred while sending request ... instead of the SDK's typed ClientException or ServerException.

Evidence

  • src/Providers/Http/HttpTransporter.php: send() selects sendWithGuzzle() whenever merged request options are present and the client matches Guzzle's send(RequestInterface, array) shape.
  • src/Providers/Http/HttpTransporter.php: buildGuzzleOptions() translates timeout and redirect options but does not disable Guzzle's HTTP-status exceptions.
  • ResponseUtil can only translate the response after HttpTransporter::send() returns it.
  • WordPress/php-ai-client#209 added the 529 => Overloaded message, but it only applies when the 529 response reaches ResponseUtil.
  • The original observed failure is tracked downstream at https://github.com/WordPress/ai-provider-for-anthropic/issues/3.

The request-options path originated in #99 and PR #109. This report is specifically about preserving PSR-18-style response semantics on that Guzzle-specific options path.

Expected behavior

Guzzle-backed requests with RequestOptions should return HTTP error responses to the SDK. Provider code can then pass those responses to ResponseUtil, which throws the appropriate typed SDK exception. Network and transport failures should continue to use the existing exception path.

Proposed implementation

Files to modify
  • src/Providers/Http/HttpTransporter.php — pass http_errors => false in the Guzzle options produced by buildGuzzleOptions().
  • tests/unit/Providers/Http/HttpTransporterTest.php — add regression coverage for the option and an HTTP error response.
  • tests/mocks/GuzzleLikeClient.php — only if needed to model Guzzle's status-error behavior in the focused test.
Complete write surface
  • Callers/readers: HttpTransporter::send(), sendWithGuzzle(), and providers that subsequently call ResponseUtil::throwIfNotSuccessful().
  • Mutation path: HttpTransporter::buildGuzzleOptions() is the single Guzzle option translation path.
  • Tests/fixtures: HttpTransporterTest and GuzzleLikeClient; ResponseUtilTest already covers typed status translation independently.
  • Schemas/config/generated artifacts: none found; this changes an internal Guzzle option only.
Compatibility and hazards
  • Apply http_errors only to the Guzzle-shaped send() path; ClientWithOptionsInterface and ordinary PSR-18 sendRequest() paths should remain unchanged.
  • Disabling Guzzle status exceptions aligns this path with PSR-18 semantics, where 4xx/5xx responses are returned rather than treated as transport failures.
  • Do not add automatic retry behavior in this change.
  • Preserve timeout, connect-timeout, redirect, and network-exception behavior.

Acceptance criteria

  • A Guzzle-shaped client called with non-null RequestOptions receives http_errors => false.
  • A simulated 529 response is returned by HttpTransporter and can be translated by ResponseUtil into ServerException with code 529 and the Overloaded message.
  • A representative 4xx response no longer takes the generic HTTP client error occurred path.
  • Existing timeout, redirect, successful-response, and network-exception tests continue to pass.

Verification

vendor/bin/phpunit tests/unit/Providers/Http/HttpTransporterTest.php
vendor/bin/phpunit tests/unit/Providers/Http/Util/ResponseUtilTest.php
composer lint

AI disclosure

This issue was investigated and drafted interactive OpenCode session.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.