Opened 7 days ago
Last modified 14 hours ago
#65505 new defect (bug)
WP_AI_Client_Prompt_Builder catches Exception but not Error, so a TypeError fatals the request
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Milestone: | 7.1 | Priority: | normal |
| Severity: | normal | Version: | trunk |
| Component: | AI | Keywords: | has-patch has-unit-tests needs-testing has-test-info |
| Focuses: | php-compatibility | Cc: |
Description
WP_AI_Client_Prompt_Builder documents that "as soon as any exception is caught in a chain of method calls, the returned instance will be in an error state … the WP_Error will be returned" (class docblock). To honor that, both the constructor and the magic __call() proxy wrap their work in try/catch.
However, both only catch ( Exception $e ):
- src/wp-includes/ai-client/class-wp-ai-client-prompt-builder.php:188 (constructor)
- src/wp-includes/ai-client/class-wp-ai-client-prompt-builder.php:361 (call)
__call() forwards the caller's arguments straight into the strict-typed php-ai-client SDK (usingTemperature(float), usingMaxTokens(int), withText(string), … — the SDK declares strict_types=1). When a caller passes an argument of an incompatible type, PHP throws a TypeError.
TypeError extends Error, not Exception, so the existing catch does not match.
The error escapes uncaught and produces a PHP fatal / HTTP 500, instead of being converted into the WP_Error the contract promises and the fluent error-state machinery never engages.
Steps to reproduce:
<?php $builder = wp_ai_client_prompt( 'Write a haiku' ); // Array can never be coerced to float -> TypeError from usingTemperature(). $result = $builder->using_temperature( array( 0.7 ) )->generate_text(); // Expected: WP_Error. Actual: uncaught TypeError -> fatal.
Note the builder file itself runs in coercive typing mode, so numeric strings are still coerced; the fatal occurs for genuinely uncoercible types (arrays, objects, non-numeric strings such as using_max_tokens( 'lots' )).
Fix: catch Throwable instead of Exception in both locations, and widen exception_to_wp_error() to accept Throwable. Its existing instanceof ladder already falls through to the generic prompt_builder_error / 500 case, so any Error maps cleanly to a WP_Error with no further change.
Change History (6)
This ticket was mentioned in PR #12256 on WordPress/wordpress-develop by @khokansardar.
7 days ago
#1
#2
@
4 days ago
@westonruter or @peterwilsoncc, how would this change play out under WordPress's strict backward compatibility policy? Everything is private, so we should be good to extend support to Throwable.
#4
@
4 days ago
hi @gziolo , could you please take a look at this issue and the associated PR when you have a chance? https://github.com/WordPress/wordpress-develop/pull/12256
@alaminfirdows commented on PR #12256:
15 hours ago
#5
To make it more readable and consistent, I prefer using $th instead of $e or $throwable. We already use $th in many other places, so it also keeps the naming consistent.
#6
@
14 hours ago
- Keywords has-test-info added
Tested on macOS (Darwin 24.6.0) against the current wordpress-develop branch.
Environment
- OS: macOS 15 (Darwin 24.6.0)
- Local environment: WordPress Develop
- WP-CLI: Available
- Test method:
wp eval-file
What I tested
Verified that the prompt builder correctly handles invalid argument types passed to using_temperature().
Result
✅ Confirmed that the TypeError is caught and converted into a WP_Error (prompt_builder_error) instead of causing an uncaught fatal error (HTTP 500).
The error handling behaves as expected and I did not observe any regressions during this verification.
Thanks for the fix!
WP_AI_Client_Prompt_Builder's contract is that any failure during a method chain puts the builder into an error state and is surfaced as aWP_Errorfrom a generating method. The constructor and the__call()proxy enforce this with try/catch, but both only caughtException.__call()forwards caller arguments into the strict-typed php-ai-client SDK (usingTemperature(float),usingMaxTokens(int), etc. — the SDK usesstrict_types=1). A wrong argument type throws aTypeError, which extendsError, notException. The existing catch missed it, so it escaped uncaught and fataled the request (HTTP 500) instead of returning aWP_Error.### Steps to reproduce
$builder = wp_ai_client_prompt( 'Write a haiku' ); // An array can never be coerced to float -> TypeError from usingTemperature(). $result = $builder->using_temperature( array( 0.7 ) )->generate_text(); // Expected: WP_Error. Before this change: uncaught TypeError -> fatal.Changes
Testing instructions
Run the AI Client suite:
npm run test:php -- --group ai-client
181 tests, 463 assertions, all passing.
Trac ticket: https://core.trac.wordpress.org/ticket/65505
## Use of AI Tools
AI assistance: Yes
Tool(s): Claude
Used for: Used for test cases. Reviewed and edited by me.