Skip to content

Real-time Collaboration: persistCRDTDoc sends entire entity record, causing 400 errors on valid posts #77049

@chubes4

Description

@chubes4

Description

When Real-Time Collaboration is enabled (wp_collaboration_enabled = true), persistCRDTDoc in @wordpress/core-data sends the entire edited entity record back to the server on page load. This causes 400 errors when any field in the record doesn't pass strict REST schema validation — even if the post is perfectly valid and functional.

The issue is in packages/core-data/src/entities.js (built as build/scripts/core-data/index.js around line 5908):

persistCRDTDoc: () => {
  resolveSelect2.getEditedEntityRecord(kind, name, key).then((editedRecord) => {
    const { meta, status } = editedRecord;
    if ("auto-draft" === status || !meta) {
      return;
    }
    dispatch3.saveEntityRecord(
      kind,
      name,
      editedRecord  // ← sends the ENTIRE record
    );
  });
},

saveEntityRecord with a full record hits the non-autosave path, which sends everything as PUT /wp/v2/posts/{id} — including readonly fields, computed fields, and values that the REST API returns on GET but rejects on write.

Step-by-step reproduction instructions

  1. Enable Real-Time Collaboration (Settings → Writing → "Enable real-time collaboration", or set wp_collaboration_enabled to 1)
  2. In Settings → Discussion, uncheck "Allow link notifications from other blogs (pingbacks and trackbacks) on new posts" and save
  3. Create a new post and publish it
  4. Open that post in the block editor
  5. Open browser DevTools → Network tab
  6. Observe a POST /wp-json/wp/v2/posts/{id}?_locale=user request returning 400 Bad Request

The error response:

{
    "code": "rest_invalid_param",
    "message": "Invalid parameter(s): ping_status",
    "data": {
        "status": 400,
        "params": {
            "ping_status": "ping_status is not one of open and closed."
        }
    }
}

This happens because:

  • WordPress core saves default_ping_status as "" (empty string) when the checkbox is unchecked (see related Core ticket below)
  • The REST API returns ping_status: "" on GET
  • persistCRDTDoc sends it back on POST
  • REST schema validation rejects "" because the enum is ["open", "closed"]

Why this is a Gutenberg bug (independent of the Core data issue)

persistCRDTDoc only needs to persist meta._crdt_document. Sending the entire entity record is:

  • Wasteful — unnecessary data over the wire
  • Fragile — any plugin registering a REST field with a schema mismatch (e.g. Co-Authors Plus returns objects where taxonomy schema expects integers) will also cause 400 errors
  • Incorrect — readonly fields like guid, generated_slug, class_list should never be sent back to the server

The autosave path in saveEntityRecord already filters to just title, excerpt, content, metapersistCRDTDoc should do the same, or better yet, only send { meta: { _crdt_document: value } }.

Related

  • WordPress Core: default_ping_status saves as empty string when unchecked (Trac #51842, closed as worksforme in 2024 — this issue now provides a clear reproduction path)
  • Co-Authors Plus #1217 — similar REST field schema collision exposed by the same round-trip behavior

Environment info

  • WordPress 6.9.4
  • Gutenberg 22.8.2
  • PHP 8.4
  • MariaDB (default NOT NULL VARCHAR behavior converts NULL → empty string)

Please confirm that you have searched existing issues in the repo.

  • Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

  • Yes

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Metadata

Metadata

Assignees

Labels

[Feature] Real-time CollaborationPhase 3 of the Gutenberg roadmap around real-time collaboration[Status] In ProgressTracking issues with work in progress[Type] BugAn existing feature does not function as intended

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions