diff --git a/src/wp-includes/class-wp-block-type.php b/src/wp-includes/class-wp-block-type.php
index 86f0ea21a2a3c..18170104cb9c5 100644
--- a/src/wp-includes/class-wp-block-type.php
+++ b/src/wp-includes/class-wp-block-type.php
@@ -510,6 +510,20 @@ public function prepare_attributes_for_render( $attributes ) {
$schema = $this->attributes[ $attribute_name ];
+ // Block metadata supports "rich-text" for editor attributes, but
+ // REST validation expects a valid JSON Schema type.
+ if ( isset( $schema['type'] ) ) {
+ if ( 'rich-text' === $schema['type'] ) {
+ $schema['type'] = 'string';
+ } elseif ( is_array( $schema['type'] ) ) {
+ foreach ( $schema['type'] as $type_index => $type ) {
+ if ( 'rich-text' === $type ) {
+ $schema['type'][ $type_index ] = 'string';
+ }
+ }
+ }
+ }
+
// Validate value by JSON schema. An invalid value should revert to
// its default, if one exists. This occurs by virtue of the missing
// attributes loop immediately following. If there is not a default
diff --git a/tests/phpunit/tests/blocks/wpBlockType.php b/tests/phpunit/tests/blocks/wpBlockType.php
index a73efa8ce8a7d..bb35b672c3dfc 100644
--- a/tests/phpunit/tests/blocks/wpBlockType.php
+++ b/tests/phpunit/tests/blocks/wpBlockType.php
@@ -254,6 +254,61 @@ public function test_prepare_attributes() {
);
}
+ /**
+ * @ticket 61406
+ */
+ public function test_prepare_attributes_validates_rich_text_attributes_as_strings() {
+ $doing_it_wrong = array();
+ $callback = static function ( $function_name ) use ( &$doing_it_wrong ) {
+ if ( 'rest_validate_value_from_schema' === $function_name ) {
+ $doing_it_wrong[] = $function_name;
+ }
+ };
+
+ add_action( 'doing_it_wrong_run', $callback );
+
+ try {
+ $block_type = new WP_Block_Type(
+ 'core/fake',
+ array(
+ 'attributes' => array(
+ 'content' => array(
+ 'type' => 'rich-text',
+ ),
+ 'caption' => array(
+ 'type' => 'rich-text',
+ 'default' => 'Default caption',
+ ),
+ 'nullableContent' => array(
+ 'type' => array( 'rich-text', 'null' ),
+ ),
+ ),
+ )
+ );
+
+ $prepared_attributes = $block_type->prepare_attributes_for_render(
+ array(
+ 'content' => 'include',
+ 'caption' => 5,
+ 'nullableContent' => 'nullable include',
+ )
+ );
+ } finally {
+ remove_action( 'doing_it_wrong_run', $callback );
+ }
+
+ $this->assertSame( 'include', $prepared_attributes['content'] );
+ $this->assertSame( 'Default caption', $prepared_attributes['caption'] );
+ $this->assertSame( 'nullable include', $prepared_attributes['nullableContent'] );
+ $this->assertSame(
+ array(),
+ $doing_it_wrong,
+ 'rest_validate_value_from_schema() should not emit _doing_it_wrong() for rich-text attributes.'
+ );
+ $this->assertSame( 'rich-text', $block_type->attributes['content']['type'] );
+ $this->assertSame( array( 'rich-text', 'null' ), $block_type->attributes['nullableContent']['type'] );
+ }
+
/**
* @ticket 45145
*/