wppaste
WordPress

rest_ensure_response( WP_REST_Response|WP_Error|WP_HTTP_Response|mixed $response ): WP_REST_Response|WP_Error

Since
4.4.0
Source
wp-includes/rest-api.php:678

Wraps a REST callback's return value in a WP_REST_Response object so WP_REST_Server::dispatch() can call methods like set_status() on it. It passes an existing WP_REST_Response through untouched, rebuilds a WP_HTTP_Response by copying its data, status, and headers, and leaves a WP_Error alone so callers can still check for it with is_wp_error(). It doesn't validate or sanitize the underlying data, so it's meant as the last step of a REST callback rather than a substitute for building the response correctly in the first place.

Ensures a REST response is a response object (for consistency).

Description

This implements WP_REST_Response, allowing usage of set_status/header/etc without needing to double-check the object. Will also allow WP_Error to indicate error responses, so users should immediately check for this value.

Compatibility

WordPress
since 4.4.0
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$responseWP_REST_Response|WP_Error|WP_HTTP_Response|mixed
Response to check.

Return value

WP_REST_Response|WP_Error
If response generated an error, WP_Error, if response is already an instance, WP_REST_Response, otherwise returns a new WP_REST_Response instance.

Code examples

Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.

Normalize a REST callback's array return value into a WP_REST_Response

A REST callback often just returns a plain array; run it through rest_ensure_response to see what it becomes.

$raw_data = array(
	'post_id' => 2,
	'price'   => get_post_meta( 2, 'price', true ),
);

$response = rest_ensure_response( $raw_data );

printf(
	'Response class: %s, status: %d, data: %s',
	esc_html( get_class( $response ) ),
	esc_html( (string) $response->get_status() ),
	esc_html( wp_json_encode( $response->get_data() ) )
);

get_status() returns 200 by default because rest_ensure_response never sets a status when it builds a new WP_REST_Response.

Let a REST callback return either data or a WP_Error and normalize both

This mirrors how a real endpoint callback works, returning an array on success or a WP_Error on failure, then passing both through rest_ensure_response.

function example_rest_lookup_callback( $post_id ) {
	$post = get_post( absint( $post_id ) );

	if ( ! $post ) {
		return new WP_Error( 'not_found', 'No post with that ID.', array( 'status' => 404 ) );
	}

	return array( 'title' => $post->post_title );
}

$missing = rest_ensure_response( example_rest_lookup_callback( 999 ) );
$found   = rest_ensure_response( example_rest_lookup_callback( 1 ) );

printf( 'Missing post result class: %s' . "\n", esc_html( get_class( $missing ) ) );
printf(
	'Found post result class: %s, data: %s',
	esc_html( get_class( $found ) ),
	esc_html( wp_json_encode( $found->get_data() ) )
);

Because a WP_Error is returned unchanged, code that calls rest_ensure_response still has to check is_wp_error() before calling response-only methods like get_status().

Common problems and fixes · 4

Why does rest_ensure_response still give me back a WP_Error instead of a response object?

The function checks is_wp_error() first and returns the error object unmodified, it never converts a WP_Error into a WP_REST_Response. Any code calling it has to keep checking for errors afterward. - Call is_wp_error() on the result before using get_status() or get_data() - Use rest_convert_error_to_response() if you specifically need the error turned into a response

Why can't I set a custom status code or headers after calling rest_ensure_response?

When you pass in a plain array or scalar, the function builds new WP_REST_Response( $response ) with no status or header arguments, so it defaults to a 200 with no extra headers.
Call set_status() or header() on the returned object yourself, or build and return your own WP_REST_Response instance from the callback instead of a raw array.

Does rest_ensure_response sanitize or validate the data I return from my endpoint?

No, looking at the source it only checks the type of $response (WP_Error, WP_REST_Response, WP_HTTP_Response, or anything else) and wraps accordingly, it never touches the values inside the data array. Sanitize and escape your own data before returning it from the callback.

Why does my WP_HTTP_Response lose custom behavior after this runs?

The function only copies get_data(), get_status(), and get_headers() from a WP_HTTP_Response into a fresh WP_REST_Response, per the comment in the source about WP_HTTP_Response lacking methods WP_REST_Server::dispatch() needs. Any extra methods or properties on your original object are dropped, so return a WP_REST_Response directly if you need more than data, status, and headers preserved.

Alternatives and related functions

WP_REST_Response
When you already know you need full control over status codes, headers, and links, construct WP_REST_Response directly instead of relying on the default wrapping.
rest_convert_error_to_response
When you specifically want a WP_Error turned into a WP_REST_Response rather than left untouched, use this instead since rest_ensure_response passes errors through unchanged.
is_wp_error
When you need to branch on whether a REST callback's return value is an error before deciding how to handle it, check this before or after calling rest_ensure_response.
WP_HTTP_Response
When writing code that only needs data, status, and headers and isn't REST-specific, use this lighter base class instead of WP_REST_Response.

Performance profile

How much work a call to rest_ensure_response() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Trivial

Touches nothing outside its own arguments.

Scaling
Constant

No loop in the body: the same number of instructions runs whatever you pass in.

Instructions
6–21

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 27.

Plugin surface
None

Nothing here hands control to plugin code.

Called by
50

50 places in core call this, so the cost is paid more often than your own code shows.

What it touches

  • hookthird-party callbacksdo_action()one call below rest_ensure_response()

What one call costs · 2 distinct outcomes

One number would be a lie: the work depends on which branch runs. These are every distinct cost rest_ensure_response() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
always6–13is_wp_error()
!is_wp_error() && !($response instanceof) && $response instanceof21is_wp_error(), ->get_data(), ->get_status(), ->get_headers()

Across PHP versions

Compiles the same on PHP 7.4, 8.1, 8.2, 8.3, 8.4, 8.5 and 8.6-dev: 27 instructions, 6–21 executed per call, 3 branches. The work does not change between versions.

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Uses · 2

  • is_wp_error()Checks whether the given variable is a WordPress Error.
  • WP_REST_Response::__construct()

Used by · 50

Show all 50

Source code

function rest_ensure_response( $response ) {	if ( is_wp_error( $response ) ) {		return $response;	} 	if ( $response instanceof WP_REST_Response ) {		return $response;	} 	/*	 * While WP_HTTP_Response is the base class of WP_REST_Response, it doesn't provide	 * all the required methods used in WP_REST_Server::dispatch().	 */	if ( $response instanceof WP_HTTP_Response ) {		return new WP_REST_Response(			$response->get_data(),			$response->get_status(),			$response->get_headers()		);	} 	return new WP_REST_Response( $response );}

Changelog

Introduced in 4.4.0. Unchanged from 6.7.7 through 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

About this page

Parsed data
Generated from the wordpress-develop 6.8.8 tag, from src/wp-includes/rest-api.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.