[wp-trac] [WordPress Trac] #64926: REST API: GET requests fail object/array schema validation when params are JSON-serialized strings

WordPress Trac noreply at wordpress.org
Tue Mar 24 13:39:06 UTC 2026


#64926: REST API: GET requests fail object/array schema validation when params are
JSON-serialized strings
-------------------------------------+-------------------------------------
 Reporter:  dsmy                     |       Owner:  (none)
     Type:  defect (bug)             |      Status:  new
 Priority:  normal                   |   Milestone:  Awaiting Review
Component:  REST API                 |     Version:  trunk
 Severity:  normal                   |  Resolution:
 Keywords:  needs-patch reporter-    |     Focuses:  javascript, rest-api,
  feedback                           |  php-compatibility
-------------------------------------+-------------------------------------
Changes (by westonruter):

 * keywords:  needs-patch => needs-patch reporter-feedback


Old description:

> == The problem
>
> When a REST endpoint declares a parameter with "type": "object" or
> "type": "array", GET requests cannot pass that parameter correctly.
> URLSearchParams has no native way to encode nested structures, so the
> only option is JSON.stringify(), producing ?input={"post_id":123}.
>
> PHP populates $_GET['input'] as a raw string.
> rest_validate_value_from_schema() receives a string where the schema
> expects object, and returns a WP_Error, the request is rejected before
> rest_sanitize_value_from_schema() can coerce it.
>
> rest_sanitize_value_from_schema() already handles this case correctly
> (calls json_decode() on strings before validating type). The fix is to
> apply the same coercion in rest_validate_value_from_schema(), or to add a
> pre-validation JSON-decode pass for GET params that match an object/array
> schema, mirroring what parse_json_params() does for application/json body
> requests.
>
> Affected file: wp-includes/rest-api.php —
> rest_validate_value_from_schema()
>
> Steps to reproduce:
>
> Register an endpoint with a param declared as "type": "object" Call it
> via GET with ?param{"key":"value"}
>
> Receive a 400: "param is not of type object"
>
> Workaround(see example below): Force POST method, or manually
> json_decode() the param in the endpoint callback before use.
>
> The closest historical ticket is
> [#42961](https://core.trac.wordpress.org/ticket/42961) ("REST API: Cannot
> pass empty object url encoded data", closed fixed in 2017) but that was
> about PHP bracket-notation encoding of empty arrays, not the
> `JSON.stringify` + `rest_validate_value_from_schema` ordering problem.
>
> == Workaround
>
> Until a core fix lands, the following filter coerces JSON-string GET
> params before validation runs:
>
> {{{#!php
> <?php
> add_filter( 'rest_request_before_callbacks', function( $response,
> $handler, $request ) {
>     if ( 'GET' !== $request->get_method() ) {
>         return $response;
>     }
>     foreach ( $handler['args'] ?? array() as $key => $arg_schema ) {
>         $type = $arg_schema['type'] ?? '';
>         if ( in_array( $type, array( 'object', 'array' ), true ) ) {
>             $value = $request->get_param( $key );
>             if ( is_string( $value ) ) {
>                 $decoded = json_decode( $value, true );
>                 if ( null !== $decoded ) {
>                     $request->set_param( $key, $decoded );
>                 }
>             }
>         }
>     }
>     return $response;
> }, 10, 3 );
> }}}
>
> The proper fix belongs in `rest_validate_value_from_schema()` in `wp-
> includes/rest-api.php`, mirroring the json_decode coercion already
> present in `rest_sanitize_value_from_schema()`.

New description:

 == The problem

 When a REST endpoint declares a parameter with "type": "object" or "type":
 "array", GET requests cannot pass that parameter correctly.
 URLSearchParams has no native way to encode nested structures, so the only
 option is JSON.stringify(), producing ?input={"post_id":123}.

 PHP populates `$_GET['input']` as a raw string.
 rest_validate_value_from_schema() receives a string where the schema
 expects object, and returns a WP_Error, the request is rejected before
 rest_sanitize_value_from_schema() can coerce it.

 rest_sanitize_value_from_schema() already handles this case correctly
 (calls json_decode() on strings before validating type). The fix is to
 apply the same coercion in rest_validate_value_from_schema(), or to add a
 pre-validation JSON-decode pass for GET params that match an object/array
 schema, mirroring what parse_json_params() does for application/json body
 requests.

 Affected file: wp-includes/rest-api.php —
 rest_validate_value_from_schema()

 Steps to reproduce:

 Register an endpoint with a param declared as "type": "object" Call it via
 GET with ?param{"key":"value"}

 Receive a 400: "param is not of type object"

 Workaround(see example below): Force POST method, or manually
 json_decode() the param in the endpoint callback before use.

 The closest historical ticket is
 [#42961](https://core.trac.wordpress.org/ticket/42961) ("REST API: Cannot
 pass empty object url encoded data", closed fixed in 2017) but that was
 about PHP bracket-notation encoding of empty arrays, not the
 `JSON.stringify` + `rest_validate_value_from_schema` ordering problem.

 == Workaround

 Until a core fix lands, the following filter coerces JSON-string GET
 params before validation runs:

 {{{#!php
 <?php
 add_filter( 'rest_request_before_callbacks', function( $response,
 $handler, $request ) {
     if ( 'GET' !== $request->get_method() ) {
         return $response;
     }
     foreach ( $handler['args'] ?? array() as $key => $arg_schema ) {
         $type = $arg_schema['type'] ?? '';
         if ( in_array( $type, array( 'object', 'array' ), true ) ) {
             $value = $request->get_param( $key );
             if ( is_string( $value ) ) {
                 $decoded = json_decode( $value, true );
                 if ( null !== $decoded ) {
                     $request->set_param( $key, $decoded );
                 }
             }
         }
     }
     return $response;
 }, 10, 3 );
 }}}

 The proper fix belongs in `rest_validate_value_from_schema()` in `wp-
 includes/rest-api.php`, mirroring the json_decode coercion already present
 in `rest_sanitize_value_from_schema()`.

--

Comment:

 Thank you for the ticket.

 > When a REST endpoint declares a parameter with "type": "object" or
 "type": "array", GET requests cannot pass that parameter correctly.
 URLSearchParams has no native way to encode nested structures, so the only
 option is JSON.stringify(), producing ?input={"post_id":123}.

 While `URLSearchParams` may not know how to encode that, doesn't PHP have
 a convention for this?

 To pass an array as `foo`, you can do `?foo[]=1&foo[]=2&foo[]=3`.

 To pass an object:`?user[name]=Bob&user[pet]=cat`.

 Does this not work?

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/64926#comment:3>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list