@@ -1476,11 +1476,16 @@ private function get_updates( $assoc_args ) {
14761476 // This is necessary because tests and plugins may use pre_http_request to mock responses
14771477 add_filter ( 'pre_http_request ' , [ $ this , 'capture_version_check_error ' ], PHP_INT_MAX , 3 );
14781478
1479+ // Also hook into http_api_debug to capture errors from real HTTP requests
1480+ // This fires when pre_http_request doesn't short-circuit the request
1481+ add_action ( 'http_api_debug ' , [ $ this , 'capture_version_check_error_from_response ' ], 10 , 5 );
1482+
14791483 try {
14801484 wp_version_check ( [], $ force_check );
14811485 } finally {
1482- // Ensure the hook is always removed, even if wp_version_check() throws an exception
1486+ // Ensure the hooks are always removed, even if wp_version_check() throws an exception
14831487 remove_filter ( 'pre_http_request ' , [ $ this , 'capture_version_check_error ' ], PHP_INT_MAX );
1488+ remove_action ( 'http_api_debug ' , [ $ this , 'capture_version_check_error_from_response ' ], 10 );
14841489 }
14851490
14861491 /**
@@ -1575,6 +1580,47 @@ public function capture_version_check_error( $response, $args, $url ) {
15751580 return $ response ;
15761581 }
15771582
1583+ /**
1584+ * Handles the http_api_debug action to capture HTTP errors from real requests.
1585+ *
1586+ * This fires when pre_http_request doesn't short-circuit the request.
1587+ * Uses the http_api_debug action hook signature.
1588+ *
1589+ * @param array|WP_Error $response HTTP response or WP_Error object.
1590+ * @param string $context Context of the HTTP request.
1591+ * @param string $_class HTTP transport class name (unused).
1592+ * @param array $_args HTTP request arguments (unused).
1593+ * @param string $url URL being requested.
1594+ */
1595+ public function capture_version_check_error_from_response ( $ response , $ context , $ _class , $ _args , $ url ) {
1596+ if ( false === strpos ( $ url , 'api.wordpress.org/core/version-check ' ) ) {
1597+ return ;
1598+ }
1599+
1600+ // Only capture on response, not pre_response
1601+ if ( 'response ' !== $ context ) {
1602+ return ;
1603+ }
1604+
1605+ // Store the error if the response is a WP_Error
1606+ if ( is_wp_error ( $ response ) ) {
1607+ $ this ->version_check_error = $ response ;
1608+ } elseif ( is_array ( $ response ) && isset ( $ response ['response ' ]['code ' ] ) && $ response ['response ' ]['code ' ] >= 400 ) {
1609+ // HTTP error status code (4xx or 5xx) - convert to WP_Error for consistency
1610+ $ this ->version_check_error = new \WP_Error (
1611+ 'http_request_failed ' ,
1612+ sprintf (
1613+ 'HTTP request failed with status %d: %s ' ,
1614+ $ response ['response ' ]['code ' ],
1615+ isset ( $ response ['response ' ]['message ' ] ) ? $ response ['response ' ]['message ' ] : 'Unknown error '
1616+ )
1617+ );
1618+ } else {
1619+ // Clear any previous error if we got a successful response
1620+ $ this ->version_check_error = null ;
1621+ }
1622+ }
1623+
15781624 /**
15791625 * Clean up extra files.
15801626 *
0 commit comments