ERROR · 23 · VERIFIED 2026-10allwpcare.com/errors/cannot-use-object-of-type-wp-error-as-array/
Errors / cannot-use-object-of-type-wp-error-as-array

Fatal Error: Cannot Use Object of Type WP_Error as Array

A plugin read a failed WordPress call as if it worked. Switch it off, read the WP_Error message to find the cause, then add is_wp_error() before the array.

The message you see
1−Fatal error: Uncaught Error: Cannot use object of type WP_Error as array
Last verified Oct 2026Tested with WordPress 7.1Applies to WordPress 7.1 (tested) · PHP 7.4, 8.1 and 8.3

Why it happens

  • A plugin or theme asked WordPress for something, the call failed, and the code then read the answer as an array ($response['body']). A failed call returns a WP_Error object, and PHP cannot read an object like an array.
  • The failed call is most often an outgoing web request: wp_remote_get() or wp_remote_post(). The server could not look up the host (cURL error 6), could not connect (7), timed out (28), or WP_HTTP_BLOCK_EXTERNAL blocks outgoing requests in wp-config.php.
  • Many other core functions return a WP_Error too: get_the_terms() and get_terms() for a taxonomy that does not exist, wp_insert_term() for a name that exists, wp_insert_post() called with its error flag, wp_create_user() for a taken user name.
  • It is a bug in the plugin or theme, even when something outside it started the failure. Code that makes a web request has to cope with the request failing.

How to fix it

Try the fixes in this order. Each one is independent: stop as soon as the error is gone.

FIX 01

Find the file and line in the message

  1. The message ends with a path and a line: ... as array in /var/www/html/wp-content/plugins/rates-widget/rates-widget.php:7. The folder after plugins/ or themes/ is the plugin or theme at fault. A path under mu-plugins/ is a must-use plugin.
  2. On a live site the message is hidden and visitors see "There has been a critical error on this website." Turn on logging in wp-config.php with define( 'WP_DEBUG', true );, define( 'WP_DEBUG_LOG', true ); and define( 'WP_DEBUG_DISPLAY', false );. The same line is written to wp-content/debug.log. I tested this: the log held PHP Fatal error: Uncaught Error: Cannot use object of type WP_Error as array in .../rates-widget.php:7.
  3. Turn debugging off again when you have the line. See the debug log guide.
FIX 02

Get the site back by switching off that plugin

  1. With WP-CLI: wp plugin deactivate rates-widget. Use the folder name from the path. In my test the page that returned status 500 returned 200 straight after.
  2. Without WP-CLI: in your host's file manager or over SFTP, rename wp-content/plugins/rates-widget to rates-widget.off. WordPress stops loading it. I tested the rename too: the page went from 500 to 200, and the plugin showed as inactive.
  3. For a theme, rename its folder in wp-content/themes. WordPress falls back to another installed theme.
  4. Then work on the cause below. Reactivating the plugin without a fix brings the error straight back.
FIX 03

Read the real reason from the WP_Error

  1. The fatal error hides the reason the call failed. Ask the object: $response->get_error_code() and $response->get_error_message(). The table in this page lists what I got for each cause.
  2. To see it without editing the plugin, run the same call from WP-CLI: wp eval '$r = wp_remote_get( "https://api.example.com/rates.json" ); echo is_wp_error( $r ) ? $r->get_error_message() : "ok";'. Put the address from the plugin's code in place of the example.
  3. If the message starts with "cURL error", go to cURL error 28 for timeouts or Download failed. cURL error for a name lookup or connection that fails.
FIX 04

Lift the block on outgoing requests

  1. If the message is "User has blocked requests through HTTP to the URL: ...", open wp-config.php and look for define( 'WP_HTTP_BLOCK_EXTERNAL', true );. A host, a security plugin or an earlier developer may have added it.
  2. Either remove the line, or keep the block and allow the one host the plugin needs: define( 'WP_ACCESSIBLE_HOSTS', 'api.example.com' );. Wildcards such as *.example.com work.
  3. I tested both on the same site and the same unfixed plugin. With the block on and no allow list the page returned 500. With WP_ACCESSIBLE_HOSTS set to the host, and again with the block removed, the page returned 200 and showed the rate. This changes the cause only. The plugin still has the bug.
FIX 05

Fix the code: check for WP_Error before you read the array

  1. Use this only on your own code, or send it to the plugin's developer. Do not edit a plugin from wordpress.org, because the next update erases your change.
  2. Put if ( is_wp_error( $response ) ) { ... } directly after the call, and stop or return a fallback inside it. Also check the HTTP status with wp_remote_retrieve_response_code( $response ). A 404 or 500 reply is a normal array, not a WP_Error.
  3. Read the body with wp_remote_retrieve_body( $response ). It returns an empty string when given a WP_Error, so it never crashes. Check the result before you use it.
  4. I ran the fixed version through four failures and one success. The page returned 200 every time. It showed the fallback text for a blocked request, a 404 and a failed lookup, and "EUR: 0.92" when the request worked. The reason of each failure was written to the debug log.

Some code on your site made a request or a lookup, and it failed. The code did not check, and read the answer as a list of values. A failed WordPress call gives back a WP_Error, which holds the failure and has no values to read, so PHP stops the page. The plugin or theme named in the message is the one to switch off. The failed call underneath is what you fix.

What you see#

On a live site, PHP's message is hidden and WordPress shows its general page. The status is 500, and every page that runs the code is affected:

A white box on a grey page. It says: There has been a critical error on this website. Below it is a link: Learn more about troubleshooting WordPress.
What visitors see on WordPress 7.1.4 when PHP errors are not shown. The shortcode sat in one post, and the home page lists that post, so the home page broke too.

With errors shown (many local and staging sites), you get the real message, with the file and line of the code that made the mistake:

Plain text: Fatal error: Uncaught Error: Cannot use object of type WP_Error as array in /var/www/html/wp-content/mu-plugins/rates-widget.php:7. A stack trace begins below.
The same failure with WP_DEBUG on and errors displayed. The first line is the one that matters. The stack trace under it lists what called what.

I made this with a five-line plugin that shows a currency rate from a web service. The plugin is a stand-in for any plugin that fetches data. Outgoing requests were blocked in wp-config.php:

rates-widget.php
add_shortcode( 'eur_rate', function () {	$response = wp_remote_get( 'https://api.example.com/rates.json' );	$data     = json_decode( $response['body'], true );   // line 7: the fatal error	return 'EUR: ' . esc_html( $data['EUR'] );} );

The wp-admin screens kept working, because the shortcode only runs when a page that holds it is shown. That is why you can often still log in to fix it.

How one call gives two kinds of answer#

wp_remote_get() has two possible return values. A request that finished gives an array, even when the answer is an error page. A request that could not be made or did not finish gives a WP_Error object.

Five stacked steps. 1: the call wp_remote_get. 2A: the request worked, so the result is an array and $r['body'] works. 2B: the request failed, so the result is a WP_Error object with no keys. 3: code that reads $r['body'] on that object stops with Cannot use object of type WP_Error as array. 4: the fix is to check is_wp_error() first.
The error is not about the web service. It is about code that assumes the good case.

WP_Error does not implement PHP's ArrayAccess, so square brackets are not allowed on it. I tested every way to use brackets on a WP_Error in PHP 8.3:

CodeResult
$r['body']Error: Cannot use object of type WP_Error as array
isset( $r['body'] )The same error. isset does not make it safe.
empty( $r['body'] )The same error
$r['body'] = 1The same error
count( $r )TypeError: count(): Argument #1 ($value) must be of type Countable|array, WP_Error given
wp_remote_retrieve_body( $r )An empty string, no error
wp_remote_retrieve_response_code( $r )An empty string, no error

The message is not new with PHP 8. I ran the same code on PHP 7.4.33, 8.1.34 and 8.3.36 and got the same Error each time. Since PHP 7 these failures are Error exceptions, and an uncaught one ends the request with "Fatal error: Uncaught Error". That is what makes the plugin crash instead of only warn.

What the WP_Error says#

The WP_Error has a code and a message. These are the ones I got from wp_remote_get() on WordPress 7.1.4, one per cause:

CauseCodeMessage
WP_HTTP_BLOCK_EXTERNAL is onhttp_request_not_executedUser has blocked requests through HTTP to the URL: https://api.example.com/rates.json.
Host name does not resolvehttp_request_failedcURL error 6: Could not resolve host: no-such-host.invalid
Nothing listens on that porthttp_request_failedcURL error 7: Failed to connect to 127.0.0.1 port 9 after 0 ms: Could not connect to server
The server is too slowhttp_request_failedcURL error 28: Operation timed out after 1006 milliseconds with 0 bytes received

A server that answers with 404 gave a plain array, not a WP_Error:

Output
dns      WP_Error  http_request_failed | cURL error 6: Could not resolve host: no-such-host.invalidrefused  WP_Error  http_request_failed | cURL error 7: Failed to connect to 127.0.0.1 port 9 after 0 ms: Could not connect to servertimeout  WP_Error  http_request_failed | cURL error 28: Operation timed out after 1006 milliseconds with 0 bytes received404      array     code=404 message=Not Found body=not found200      array     code=200 message=OK body={"EUR":0.92}

So is_wp_error() alone is not a full check. The 404 reply passes it, and its body is not the data you wanted. Check the status code as well.

The check that fixes it#

rates-widget.php (fixed)
add_shortcode( 'eur_rate', function () {	$response = wp_remote_get( 'https://api.example.com/rates.json' );	if ( is_wp_error( $response ) ) {		error_log( 'Rates Widget: ' . $response->get_error_code() . ': ' . $response->get_error_message() );		return 'EUR: not available right now';	}	if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {		error_log( 'Rates Widget: HTTP ' . wp_remote_retrieve_response_code( $response ) );		return 'EUR: not available right now';	}	$data = json_decode( wp_remote_retrieve_body( $response ), true );	if ( ! is_array( $data ) || ! isset( $data['EUR'] ) ) {		return 'EUR: not available right now';	}	return 'EUR: ' . esc_html( $data['EUR'] );} );

Each failure now shows the fallback text, the page stays up, and the debug log keeps the reason:

wp-content/debug.log
[30-Oct-2026 12:05:23 UTC] Rates Widget: http_request_not_executed: User has blocked requests through HTTP to the URL: https://api.example.com/rates.json.[30-Oct-2026 12:05:26 UTC] Rates Widget: HTTP 404[30-Oct-2026 12:05:30 UTC] Rates Widget: http_request_failed: cURL error 6: Could not resolve host: rates.invalid

The fix works because it moves the failure to a place the code controls. The call can still fail, and it will. The page just no longer depends on it.

The same error from other functions#

The web request is the most common source, not the only one. These all returned a WP_Error in my tests, and indexing the result with [0] gave the same fatal error:

CallCodeMessage
get_the_terms( 1, 'nope' )invalid_taxonomyInvalid taxonomy.
get_terms( array( 'taxonomy' => 'nope' ) )invalid_taxonomyInvalid taxonomy.
get_term_link( 9999, 'category' )invalid_termEmpty Term.
wp_insert_term( 'Rates', 'category' ) a second timeterm_existsA term with the name provided already exists with this parent.
wp_insert_post( array(), true )empty_contentContent, title, and excerpt are empty.
wp_create_user( 'admin', 'x', 'a@example.com' )existing_user_loginSorry, that username already exists!

Two details are worth knowing. wp_insert_post( array() ) without the true flag does not return a WP_Error. It returns 0. And a foreach over a WP_Error does not fail. It loops over the object's own properties, so you get two array values with the keys errors and error_data, and no terms. That bug shows up later as a different warning.

When it is not your code#

If the plugin comes from wordpress.org or a vendor, you cannot fix its code in a way that lasts. Do this:

  1. Switch the plugin off and get the site back (fix 2).
  2. Find why the call fails (fixes 3 and 4). Often the failure is a setting on your server, not the plugin.
  3. Tell the plugin's developer the file and line from the message, and that the code needs is_wp_error() after the call. That is a precise report they can act on in minutes.
  4. Update the plugin when they ship the fix, using the steps in how to update plugins safely.

If the plugin will not activate at all, see plugin could not be activated. If your site shows the critical error page and you cannot tell which plugin it is, see there has been a critical error on this website.

Sources

  1. WP_Error (WordPress Code Reference) · get_error_code(), get_error_message(), get_error_data()
  2. is_wp_error() (WordPress Code Reference) · True when the value is a WP_Error object
  3. wp_remote_get() (WordPress Code Reference) · Returns the response array, or a WP_Error on failure
  4. wp_remote_retrieve_body() (WordPress Code Reference) · Returns an empty string for a WP_Error
  5. Editing wp-config.php: block external URL requests · WP_HTTP_BLOCK_EXTERNAL and WP_ACCESSIBLE_HOSTS
  6. PHP 7.0 backward incompatible changes: error and exception handling · Fatal errors became Error exceptions; an uncaught one ends the script
Newsletter

One email when something breaks or changes.

New plugins and re-tested guides, about twice a month. Never a sales pitch.