Abilities: Catch exceptions thrown by ability callbacks#11544
Abilities: Catch exceptions thrown by ability callbacks#11544gziolo wants to merge 1 commit intoWordPress:trunkfrom
Conversation
Converts any `Throwable` raised by an `execute_callback` or `permission_callback` into a `WP_Error` with code `ability_callback_exception`, instead of letting it propagate uncaught. This prevents the REST ability runner from surfacing a 500 HTML fatal error page when a callback fails (e.g. an AI connector HTTP timeout), and gives clients a predictable JSON error response. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
Summary
When an ability's
execute_callbackorpermission_callbackthrows an uncaught exception, it propagates throughWP_Ability::execute()and the REST run controller, causing WordPress to render an HTML fatal error page with a 500 status instead of a JSON error response.This was first reported via the Core AI feature plugin when an AI connector's HTTP request to a remote provider timed out after 30 seconds — the underlying client threw a
ServerException, which surfaced to the browser as a full HTML page inside the REST response, breaking client-side error handling.See WordPress/ai#392 for the original report and root-cause analysis.
Fix
WP_Ability::invoke_callback()now catches anyThrowableraised by the callback and converts it to aWP_Errorwith codeability_callback_exception. Becauseinvoke_callback()is shared betweendo_execute()andcheck_permissions(), both callback types are protected by a single change, and the existingis_wp_error()handling inexecute()surfaces the error to the caller unchanged.Trac ticket: https://core.trac.wordpress.org/ticket/65058
Test plan
execute_callbackthat throws — expectsWP_Errorwith codeability_callback_exception.permission_callbackthat throws viacheck_permissions()directly — same code, same behavior.🤖 Generated with Claude Code