[PP-1] Do not allow a user Publish request while there are pending changes to the current layout
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3520592. -->
Reported by: [tedbow](https://www.drupal.org/user/240860)
Related to !931
>>>
<h3 id="overview">Overview</h3>
<p>found this from <span class="drupalorg-gitlab-issue-link project-issue-status-info project-issue-status-7"><a href="https://www.drupal.org/project/experience_builder/issues/3520067" title="Status: Closed (fixed)">#3520067: [Needs design] Confusing messages when publishing immediately after making changes: auto-save race condition</a></span><br>
@lauriii wrote</p>
<blockquote><p>
I ran into this by making a change and then quickly trying to publish it after making that change. I think the error I got was "This item is unexpected." and it was really confusing because I had no idea what is "this" in the context of the message and why it was unexpected.
</p></blockquote>
<p>I was able to reproduce this too but not consistently </p>
<p><strong>Steps to reproduce:</strong></p>
<ol>
<li>Add a heading component to the canvas.</li>
<li>Change the styling from <strong>Primary</strong> to <strong>Secondary</strong> and wait until the 'Review 1 change' button appears.</li>
<li>
Click inside the text field to edit it (while keeping your cursor near the 'Review 1 change' button), then quickly enter some text, click the review button, and immediately publish.
</li>
<li>
You will encounter a <strong>409 (Conflict)</strong> error with the message: <br><br>
<em>"An item in the publish request did not match the expected format or value. Please refresh your page and try again."</em>
<p><img src="https://www.drupal.org/files/issues/2025-06-26/Screenshot%202025-06-26%20at%205.34.36%E2%80%AFPM.png" alt="">
</p></li>
</ol>
<p><strong>Details seen in the network response:</strong></p>
<pre>
{
"detail": "An item in the publish request did not match the expected format or value. Please refresh your page and try again.",
"source": {
"pointer": "xb_page:1:en"
},
"code": 2,
"meta": {
"entity_type": "xb_page",
"entity_id": "1",
"label": "Test page",
"api_auto_save_key": "xb_page:1:en"
}
}
</pre><p>What is probably happening with "then quickly enter some text, click the review button, and immediately publish." from above:</p>
<ol>
<li>Entering the test will trigger a POST request to <code>\Drupal\experience_builder\Controller\ApiLayoutController::post</code></li>
<li>This triggers a request will result in the AutoSave entry for the entity to be updated</li>
<li>There is request to <code>\Drupal\experience_builder\Controller\ApiAutoSaveController::post()</code> which receives a 409</li>
<li><strong>After</strong> that there is request to <code>\Drupal\experience_builder\Controller\ApiAutoSaveController::get()</code></li>
</ol>
<p>I am not sure what triggers the last request to <code>ApiAutoSaveController::get()</code>. My guess would be that it is <strong>NOT</strong> clicking "review x changes" or that would happen <strong>before</strong> the request to <code>ApiAutoSaveController::post()</code> which is triggered by clicking "Publish all changes"</p>
<p>The 409 error happens because the request to <code>ApiAutoSaveController::post()</code> could not possibly have the latest auto-save hashes because there has not been a new request to <code>ApiAutoSaveController::get()</code></p>
<h3 id="proposed-resolution">Proposed resolution</h3>
<h4>Original suggestion</h4>
<p>When returning the updated layout preview also return the item's auto-save</p>
<p>Merge this on the client side with the existing list of auto-saves</p>
<h4>Updated suggestion</h4>
<p>I, tedbow, made the original suggestion. I realize now without front-end changes this likely would solve the problem. If there nothing stops the user from clicking "Publish all changes" before the request to <code>\Drupal\experience_builder\Controller\ApiLayoutController::post</code> returns it won't matter if we update the auto-save entry on the client side when the <code>ApiLayoutController::post</code> request returns because the POST request to <code>ApiAutoSaveController::post()</code> would have already been sent with the old aut-save hash.</p>
<p>The safest method I think would be </p>
<ol>
<li>When the user clicks "Review X Changes</li>
<p> disallow any new UI changes.
</p><li>Disable the "publish all changes" button</li>
<li>If there are any existing requests to update the layout then wait for them to return</li>
<li>Make a request to <code>ApiAutoSaveController::get()</code> to update the publish list</li>
<li>only then after the client data has been updated then enable the "publish all changes" button which would the request to <code>ApiLayoutController::post</code></li>
</ol>
<h3 id="ui-changes">User interface changes</h3>
> Related issue: [Issue #3522368](https://www.drupal.org/node/3522368)
> Related issue: [Issue #3526907](https://www.drupal.org/node/3526907)
> Related issue: [Issue #3521002](https://www.drupal.org/node/3521002)
issue